Compare commits

...

2 Commits

Author SHA1 Message Date
71d1f2900c Большие доработки по фискилизации
- у `AtolClient` теперь возможно получить последний отправленный запрос `getLastRequest()`
- у `AtolClient::auth()` удалены аргументы за ненадобностью
- улучшен `Client::jsonSerialize()`
- исправлен `Receipt::jsonSerialize()`
- у `Receipt` и `Correction` появились методы фискализации, вкусный сахарок
- удалён енам `DocumentTypes` за ненадобностью
- исправлены тесты монитора и документов
- рабочий фискализатор с получением результатов и покрытием
2021-12-18 14:45:00 +08:00
b4cc0fec53 Мелочи по конфигу composer 2021-12-18 14:09:07 +08:00
34 changed files with 973 additions and 336 deletions

View File

@ -75,5 +75,9 @@
"scripts": { "scripts": {
"test": "vendor/bin/phpunit --colors=always", "test": "vendor/bin/phpunit --colors=always",
"coverage": "php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html .coverage" "coverage": "php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html .coverage"
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
} }
} }

View File

@ -29,6 +29,16 @@ use JetBrains\PhpStorm\Pure;
*/ */
abstract class AtolClient abstract class AtolClient
{ {
/**
* @var array Последний запрос к серверу АТОЛ
*/
protected array $request;
/**
* @var KktResponse|null Последний ответ сервера АТОЛ
*/
protected ?KktResponse $response;
/** /**
* @var bool Флаг тестового режима * @var bool Флаг тестового режима
*/ */
@ -54,11 +64,6 @@ abstract class AtolClient
*/ */
private ?string $token = null; private ?string $token = null;
/**
* @var KktResponse|null Последний ответ сервера АТОЛ
*/
private ?KktResponse $response;
/** /**
* Конструктор * Конструктор
* *
@ -88,6 +93,26 @@ abstract class AtolClient
!is_null($password) && $this->setPassword($password); !is_null($password) && $this->setPassword($password);
} }
/**
* Возвращает последний запрос к серверу
*
* @return array
*/
public function getLastRequest(): array
{
return $this->request;
}
/**
* Возвращает последний ответ сервера
*
* @return KktResponse|null
*/
public function getLastResponse(): ?KktResponse
{
return $this->response;
}
/** /**
* Возвращает установленный флаг тестового режима * Возвращает установленный флаг тестового режима
* *
@ -132,16 +157,6 @@ abstract class AtolClient
return $this; return $this;
} }
/**
* Возвращает последний ответ сервера
*
* @return KktResponse|null
*/
public function getResponse(): ?KktResponse
{
return $this->response;
}
/** /**
* Возвращает логин доступа к API * Возвращает логин доступа к API
* *
@ -273,36 +288,29 @@ abstract class AtolClient
): KktResponse { ): KktResponse {
$http_method = strtoupper(trim($http_method)); $http_method = strtoupper(trim($http_method));
$options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []); $options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []);
if ($http_method != 'GET') { $http_method != 'GET' && $options['json'] = $data;
$options['json'] = $data; $this->request = array_merge([
} 'method' => $http_method,
'url' => $url,
], $options);
$response = $this->http->request($http_method, $url, $options); $response = $this->http->request($http_method, $url, $options);
return $this->response = new KktResponse($response); return $this->response = new KktResponse($response);
} }
/** /**
* Выполняет авторизацию на сервере АТОЛ * Выполняет авторизацию на сервере АТОЛ
*
* Авторизация выполнится только если неизвестен токен * Авторизация выполнится только если неизвестен токен
* *
* @param string|null $login
* @param string|null $password
* @return bool * @return bool
* @throws AuthFailedException * @throws AuthFailedException
* @throws TooLongLoginException
* @throws EmptyLoginException * @throws EmptyLoginException
* @throws EmptyPasswordException * @throws EmptyPasswordException
* @throws TooLongPasswordException
* @throws GuzzleException * @throws GuzzleException
*/ */
public function auth(?string $login = null, ?string $password = null): bool public function auth(): bool
{ {
if (empty($this->getToken())) { if (empty($this->getToken()) && $token = $this->doAuth()) {
!is_null($login) && $this->setLogin($login); $this->setToken($token);
!is_null($password) && $this->setPassword($password);
if ($token = $this->doAuth()) {
$this->setToken($token);
}
} }
return !empty($this->getToken()); return !empty($this->getToken());
} }
@ -320,4 +328,5 @@ abstract class AtolClient
* @return string * @return string
*/ */
abstract protected function getMainEndpoint(): string; abstract protected function getMainEndpoint(): string;
} }

View File

@ -13,30 +13,30 @@ namespace AtolOnline\Api;
use AtolOnline\{ use AtolOnline\{
Constants\Constraints, Constants\Constraints,
Entities\Company, TestEnvParams};
Entities\Document, use AtolOnline\Entities\{
Exceptions\AuthFailedException, Correction,
Exceptions\EmptyCorrectionInfoException, Receipt};
Exceptions\EmptyLoginException, use AtolOnline\Exceptions\{
Exceptions\EmptyPasswordException, AuthFailedException,
Exceptions\InvalidCallbackUrlException, EmptyGroupException,
Exceptions\InvalidDocumentTypeException, EmptyLoginException,
Exceptions\InvalidInnLengthException, EmptyPasswordException,
Exceptions\InvalidUuidException, InvalidCallbackUrlException,
Exceptions\TooLongCallbackUrlException, InvalidEntityInCollectionException,
Exceptions\TooLongLoginException, InvalidInnLengthException,
Exceptions\TooLongPasswordException, InvalidPaymentAddressException,
Exceptions\TooLongPaymentAddressException, InvalidUuidException,
Exceptions\TooManyItemsException, TooLongCallbackUrlException,
Exceptions\TooManyVatsException, TooLongLoginException,
TestEnvParams TooLongPasswordException,
}; TooLongPaymentAddressException};
use Exception;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use JetBrains\PhpStorm\Pure;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
/** /**
* Класс для регистрации документов на ККТ * Класс фискализатора для регистрации документов на ККТ
*/ */
class KktFiscalizer extends AtolClient class KktFiscalizer extends AtolClient
{ {
@ -62,6 +62,7 @@ class KktFiscalizer extends AtolClient
* @throws EmptyPasswordException * @throws EmptyPasswordException
* @throws TooLongLoginException * @throws TooLongLoginException
* @throws TooLongPasswordException * @throws TooLongPasswordException
* @throws EmptyGroupException
* @see https://guzzle.readthedocs.io/en/latest/request-options.html * @see https://guzzle.readthedocs.io/en/latest/request-options.html
*/ */
public function __construct( public function __construct(
@ -75,211 +76,201 @@ class KktFiscalizer extends AtolClient
!is_null($group) && $this->setGroup($group); !is_null($group) && $this->setGroup($group);
} }
/**
* Устанавливает группу доступа к ККТ
*
* @param string $group
* @return $this
*/
public function setGroup(string $group): self
{
// критерии к длине строки не описаны ни в схеме, ни в документации
$this->group = $group;
return $this;
}
/** /**
* Возвращает группу доступа к ККТ в соответствии с флагом тестового режима * Возвращает группу доступа к ККТ в соответствии с флагом тестового режима
* *
* @return string|null * @return string|null
*/ */
#[Pure]
public function getGroup(): ?string public function getGroup(): ?string
{ {
return $this->group; return $this->isTestMode()
? TestEnvParams::FFD105()['group']
: $this->group;
} }
/** /**
* Устанавливает URL для приёма колбеков * Устанавливает группу доступа к ККТ
* *
* @param string $url * @param string $group
* @return $this * @return $this
* @throws TooLongCallbackUrlException * @throws EmptyGroupException
* @throws InvalidCallbackUrlException
*/ */
public function setCallbackUrl(string $url): self public function setGroup(string $group): self
{ {
if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) { // критерии к длине строки не описаны ни в схеме, ни в документации
throw new TooLongCallbackUrlException($url, Constraints::MAX_LENGTH_CALLBACK_URL); empty($group = trim($group)) && throw new EmptyGroupException();
} elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) { $this->group = $group;
throw new InvalidCallbackUrlException('Callback URL not matches with pattern');
}
$this->callback_url = $url;
return $this; return $this;
} }
/** /**
* Возвращает URL для приёма колбеков * Возвращает URL для приёма колбеков
* *
* @return string * @return string|null
*/ */
public function getCallbackUrl(): string public function getCallbackUrl(): ?string
{ {
return $this->callback_url; return $this->callback_url;
} }
/**
* Устанавливает URL для приёма колбеков
*
* @param string|null $url
* @return $this
* @throws TooLongCallbackUrlException
* @throws InvalidCallbackUrlException
*/
public function setCallbackUrl(?string $url = null): self
{
$url = trim((string)$url);
if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) {
throw new TooLongCallbackUrlException($url);
} elseif (!empty($url) && !preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) {
throw new InvalidCallbackUrlException();
}
$this->callback_url = $url ?: null;
return $this;
}
/** /**
* Регистрирует документ прихода * Регистрирует документ прихода
* *
* @param Document $document Объект документа * @param Receipt $receipt Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyCorrectionInfoException * @throws EmptyLoginException
* @throws InvalidInnLengthException * @throws EmptyPasswordException
* @throws TooLongPaymentAddressException
* @throws InvalidDocumentTypeException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
public function sell(Document $document, ?string $external_id = null): KktResponse public function sell(Receipt $receipt, ?string $external_id = null): ?KktResponse
{ {
if ($document->getCorrectionInfo()) { return $this->registerDocument('sell', $receipt, $external_id);
throw new EmptyCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('sell', 'receipt', $document, $external_id);
} }
/** /**
* Регистрирует документ возврата прихода * Регистрирует документ возврата прихода
* *
* @param Document $document Объект документа * @param Receipt $receipt Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyCorrectionInfoException * @throws EmptyLoginException
* @throws InvalidInnLengthException * @throws EmptyPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyVatsException
* @throws InvalidDocumentTypeException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
public function sellRefund(Document $document, ?string $external_id = null): KktResponse public function sellRefund(Receipt $receipt, ?string $external_id = null): ?KktResponse
{ {
if ($document->getCorrectionInfo()) { return $this->registerDocument('sell_refund', $receipt, $external_id);
throw new EmptyCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id);
} }
/** /**
* Регистрирует документ коррекции прихода * Регистрирует документ коррекции прихода
* *
* @param Document $document Объект документа * @param Correction $correction Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyCorrectionInfoException * @throws EmptyLoginException
* @throws InvalidInnLengthException * @throws EmptyPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyItemsException
* @throws InvalidDocumentTypeException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
public function sellCorrection(Document $document, ?string $external_id = null): KktResponse public function sellCorrect(Correction $correction, ?string $external_id = null): ?KktResponse
{ {
if (!$document->getCorrectionInfo()) { return $this->registerDocument('sell_correction', $correction, $external_id);
throw new EmptyCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('sell_correction', 'correction', $document, $external_id);
} }
/** /**
* Регистрирует документ расхода * Регистрирует документ расхода
* *
* @param Document $document * @param Receipt $receipt Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyCorrectionInfoException * @throws EmptyLoginException
* @throws InvalidInnLengthException * @throws EmptyPasswordException
* @throws TooLongPaymentAddressException
* @throws InvalidDocumentTypeException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
public function buy(Document $document, ?string $external_id = null): KktResponse public function buy(Receipt $receipt, ?string $external_id = null): ?KktResponse
{ {
if ($document->getCorrectionInfo()) { return $this->registerDocument('buy', $receipt, $external_id);
throw new EmptyCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy', 'receipt', $document, $external_id);
} }
/** /**
* Регистрирует документ возврата расхода * Регистрирует документ возврата расхода
* *
* @param Document $document * @param Receipt $receipt Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyCorrectionInfoException * @throws EmptyLoginException
* @throws InvalidInnLengthException * @throws EmptyPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyVatsException
* @throws InvalidDocumentTypeException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
public function buyRefund(Document $document, ?string $external_id = null): KktResponse public function buyRefund(Receipt $receipt, ?string $external_id = null): ?KktResponse
{ {
if ($document->getCorrectionInfo()) { return $this->registerDocument('buy_refund', $receipt, $external_id);
throw new EmptyCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id);
} }
/** /**
* Регистрирует документ коррекции расхода * Регистрирует документ коррекции расхода
* *
* @param Document $document * @param Correction $correction Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException Ошибка авторизации * @throws AuthFailedException
* @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции * @throws EmptyLoginException
* @throws InvalidInnLengthException Некорректная длтина ИНН * @throws EmptyPasswordException
* @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов
* @throws TooManyItemsException Слишком много предметов расчёта
* @throws InvalidDocumentTypeException Некорректный тип документа
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
public function buyCorrection(Document $document, ?string $external_id = null): KktResponse public function buyCorrect(Correction $correction, ?string $external_id = null): ?KktResponse
{ {
if (!$document->getCorrectionInfo()) { return $this->registerDocument('buy_correction', $correction, $external_id);
throw new EmptyCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('buy_correction', 'correction', $document, $external_id);
} }
/** /**
* Проверяет статус чека на ККТ один раз * Проверяет статус чека на ККТ один раз
* *
* @param string $uuid UUID регистрации * @param string $uuid UUID регистрации
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyLoginException * @throws EmptyLoginException
* @throws EmptyPasswordException * @throws EmptyPasswordException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidUuidException * @throws InvalidUuidException
* @throws TooLongLoginException
* @throws TooLongPasswordException
*/ */
public function getDocumentStatus(string $uuid): KktResponse public function getDocumentStatus(string $uuid): ?KktResponse
{ {
$uuid = trim($uuid); !Uuid::isValid($uuid = trim($uuid)) && throw new InvalidUuidException($uuid);
if (!Uuid::isValid($uuid)) { return $this->auth()
throw new InvalidUuidException($uuid); ? $this->sendRequest('GET', $this->getFullUrl('report/' . $uuid))
} : null;
$this->auth();
return $this->sendRequest('GET', 'report/' . $uuid);
} }
/** /**
@ -289,16 +280,14 @@ class KktFiscalizer extends AtolClient
* @param string $uuid UUID регистрации * @param string $uuid UUID регистрации
* @param int $retry_count Количество попыток * @param int $retry_count Количество попыток
* @param int $timeout Таймаут в секундах между попытками * @param int $timeout Таймаут в секундах между попытками
* @return KktResponse * @return KktResponse|null
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyLoginException * @throws EmptyLoginException
* @throws EmptyPasswordException * @throws EmptyPasswordException
* @throws GuzzleException * @throws GuzzleException
* @throws InvalidUuidException * @throws InvalidUuidException
* @throws TooLongLoginException
* @throws TooLongPasswordException
*/ */
public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): KktResponse public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): ?KktResponse
{ {
$try = 0; $try = 0;
do { do {
@ -317,62 +306,73 @@ class KktFiscalizer extends AtolClient
* Отправляет документ на регистрацию * Отправляет документ на регистрацию
* *
* @param string $api_method Метод API * @param string $api_method Метод API
* @param string $type Тип документа: receipt, correction * @param Receipt|Correction $document Документ
* @param Document $document Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) * @return KktResponse|null
* @return KktResponse * @throws AuthFailedException
* @throws AuthFailedException Ошибка авторизации * @throws EmptyLoginException
* @throws InvalidDocumentTypeException Некорректный тип документа * @throws EmptyPasswordException
* @throws InvalidInnLengthException Некорректная длина ИНН
* @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов
* @throws GuzzleException * @throws GuzzleException
* @throws Exception * @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/ */
protected function registerDocument( protected function registerDocument(
string $api_method, string $api_method,
string $type, Receipt|Correction $document,
Document $document,
?string $external_id = null ?string $external_id = null
): KktResponse { ): ?KktResponse {
$type = trim($type); $this->isTestMode() && $document->getCompany()
if (!in_array($type, ['receipt', 'correction'])) { ->setInn(TestEnvParams::FFD105()['inn'])
throw new InvalidDocumentTypeException($type); ->setPaymentAddress(TestEnvParams::FFD105()['payment_address']);
} $this->isTestMode() && $document instanceof Receipt
$this->auth(); && $document->getClient()->setInn(TestEnvParams::FFD105()['inn']);
if ($this->isTestMode()) { $this->getCallbackUrl() && $data['service'] = ['callback_url' => $this->getCallbackUrl()];
$document->setCompany(new Company( return $this->auth()
'test@example.com', ? $this->sendRequest(
TestEnvParams::FFD105()['sno'], 'POST',
TestEnvParams::FFD105()['inn'], $this->getFullUrl($api_method),
TestEnvParams::FFD105()['payment_address'], array_merge($data ?? [], [
)); 'timestamp' => date('d.m.Y H:i:s'),
} 'external_id' => $external_id ?: Uuid::uuid4()->toString(),
$data['timestamp'] = date('d.m.y H:i:s'); $document::DOC_TYPE => $document->jsonSerialize(),
$data['external_id'] = $external_id ?: Uuid::uuid4()->toString(); ])
$data[$type] = $document; )
if ($this->getCallbackUrl()) { : null;
$data['service'] = ['callback_url' => $this->getCallbackUrl()];
}
return $this->sendRequest('POST', trim($api_method), $data);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
#[Pure]
protected function getAuthEndpoint(): string protected function getAuthEndpoint(): string
{ {
return $this->isTestMode() return $this->isTestMode()
? 'https://testonline.atol.ru/possystem/v1/getToken' ? 'https://testonline.atol.ru/possystem/v4/getToken'
: 'https://online.atol.ru/possystem/v1/getToken'; : 'https://online.atol.ru/possystem/v4/getToken';
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
#[Pure]
protected function getMainEndpoint(): string protected function getMainEndpoint(): string
{ {
return $this->isTestMode() return $this->isTestMode()
? 'https://testonline.atol.ru/possystem/v4/' ? 'https://testonline.atol.ru/possystem/v4/'
: 'https://online.atol.ru/possystem/v4/'; : 'https://online.atol.ru/possystem/v4/';
} }
/**
* Возвращает полный URL метода API
*
* @param string $api_method
* @return string
*/
#[Pure]
protected function getFullUrl(string $api_method): string
{
return $this->getMainEndpoint() . $this->getGroup() . '/' . trim($api_method);
}
} }

View File

@ -13,9 +13,11 @@ namespace AtolOnline\Api;
use AtolOnline\Entities\Kkt; use AtolOnline\Entities\Kkt;
use AtolOnline\Exceptions\{ use AtolOnline\Exceptions\{
AuthFailedException,
EmptyLoginException,
EmptyMonitorDataException, EmptyMonitorDataException,
NotEnoughMonitorDataException EmptyPasswordException,
}; NotEnoughMonitorDataException};
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use JetBrains\PhpStorm\Pure; use JetBrains\PhpStorm\Pure;
@ -54,16 +56,21 @@ class KktMonitor extends AtolClient
* *
* @param int|null $limit * @param int|null $limit
* @param int|null $offset * @param int|null $offset
* @return KktResponse * @return KktResponse|null
* @throws GuzzleException * @throws GuzzleException
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9 * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9
*/ */
protected function fetchAll(?int $limit = null, ?int $offset = null): KktResponse protected function fetchAll(?int $limit = null, ?int $offset = null): ?KktResponse
{ {
$params = []; $params = [];
!is_null($limit) && $params['limit'] = $limit; !is_null($limit) && $params['limit'] = $limit;
!is_null($offset) && $params['offset'] = $offset; !is_null($offset) && $params['offset'] = $offset;
return $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params); return $this->auth()
? $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params)
: null;
} }
/** /**
@ -72,6 +79,9 @@ class KktMonitor extends AtolClient
* @param int|null $limit * @param int|null $limit
* @param int|null $offset * @param int|null $offset
* @return Collection * @return Collection
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException * @throws GuzzleException
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9 * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9
*/ */

View File

@ -132,10 +132,10 @@ final class Client extends Entity
public function jsonSerialize(): array public function jsonSerialize(): array
{ {
$json = []; $json = [];
$this->getName() && $json['name'] = $this->getName(); !is_null($this->getName()) && $json['name'] = $this->getName();
$this->getEmail() && $json['email'] = $this->getEmail(); !is_null($this->getEmail()) && $json['email'] = $this->getEmail();
$this->getPhone() && $json['phone'] = $this->getPhone(); !is_null($this->getPhone()) && $json['phone'] = $this->getPhone();
$this->getInn() && $json['inn'] = $this->getInn(); !is_null($this->getInn()) && $json['inn'] = $this->getInn();
return $json; return $json;
} }
} }

View File

@ -10,13 +10,22 @@
namespace AtolOnline\Entities; namespace AtolOnline\Entities;
use AtolOnline\{ use AtolOnline\{
Api\KktFiscalizer,
Api\KktResponse,
Collections\Payments, Collections\Payments,
Collections\Vats, Collections\Vats,
Constants\Constraints}; Constants\Constraints};
use AtolOnline\Exceptions\{ use AtolOnline\Exceptions\{
AuthFailedException,
EmptyLoginException,
EmptyPasswordException,
InvalidEntityInCollectionException, InvalidEntityInCollectionException,
TooLongCashierException}; InvalidInnLengthException,
InvalidPaymentAddressException,
TooLongCashierException,
TooLongPaymentAddressException};
use Exception; use Exception;
use GuzzleHttp\Exception\GuzzleException;
use JetBrains\PhpStorm\ArrayShape; use JetBrains\PhpStorm\ArrayShape;
/** /**
@ -26,6 +35,11 @@ use JetBrains\PhpStorm\ArrayShape;
*/ */
class Correction extends Entity class Correction extends Entity
{ {
/**
* Тип документа
*/
public const DOC_TYPE = 'correction';
/** /**
* @var Company Продавец * @var Company Продавец
*/ */
@ -194,6 +208,46 @@ class Correction extends Entity
return $this; return $this;
} }
/**
* Регистрирует коррекцию прихода по текущему документу
*
* @param KktFiscalizer $fiscalizer Объект фискализатора
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse|null
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/
public function sellCorrect(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse
{
return $fiscalizer->sellCorrect($this, $external_id);
}
/**
* Регистрирует коррекцию расхода по текущему документу
*
* @param KktFiscalizer $fiscalizer Объект фискализатора
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse|null
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/
public function buyCorrect(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse
{
return $fiscalizer->buyCorrect($this, $external_id);
}
/** /**
* @inheritDoc * @inheritDoc
* @throws InvalidEntityInCollectionException * @throws InvalidEntityInCollectionException

View File

@ -16,6 +16,7 @@ namespace AtolOnline\Entities;
use ArrayAccess; use ArrayAccess;
use BadMethodCallException; use BadMethodCallException;
use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Arrayable;
use JetBrains\PhpStorm\ArrayShape;
use JsonSerializable; use JsonSerializable;
use Stringable; use Stringable;
@ -32,6 +33,13 @@ abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayA
/** /**
* @inheritDoc * @inheritDoc
*/ */
#[ArrayShape([
'company' => "\AtolOnline\Entities\Company",
'correction_info' => "\AtolOnline\Entities\CorrectionInfo",
'payments' => "array",
'vats' => "\AtolOnline\Collections\Vats|null",
'cashier' => "\null|string"
])]
public function toArray() public function toArray()
{ {
return $this->jsonSerialize(); return $this->jsonSerialize();

View File

@ -11,15 +11,24 @@ declare(strict_types = 1);
namespace AtolOnline\Entities; namespace AtolOnline\Entities;
use AtolOnline\Api\KktFiscalizer;
use AtolOnline\Api\KktResponse;
use AtolOnline\Collections\Items; use AtolOnline\Collections\Items;
use AtolOnline\Collections\Payments; use AtolOnline\Collections\Payments;
use AtolOnline\Collections\Vats; use AtolOnline\Collections\Vats;
use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\AuthFailedException;
use AtolOnline\Exceptions\EmptyItemsException; use AtolOnline\Exceptions\EmptyItemsException;
use AtolOnline\Exceptions\EmptyLoginException;
use AtolOnline\Exceptions\EmptyPasswordException;
use AtolOnline\Exceptions\InvalidEntityInCollectionException; use AtolOnline\Exceptions\InvalidEntityInCollectionException;
use AtolOnline\Exceptions\InvalidInnLengthException;
use AtolOnline\Exceptions\InvalidPaymentAddressException;
use AtolOnline\Exceptions\TooLongAddCheckPropException; use AtolOnline\Exceptions\TooLongAddCheckPropException;
use AtolOnline\Exceptions\TooLongCashierException; use AtolOnline\Exceptions\TooLongCashierException;
use AtolOnline\Exceptions\TooLongPaymentAddressException;
use Exception; use Exception;
use GuzzleHttp\Exception\GuzzleException;
/** /**
* Класс, описывающий документ прихода, расхода, возврата прихода, возврата расхода * Класс, описывающий документ прихода, расхода, возврата прихода, возврата расхода
@ -28,6 +37,11 @@ use Exception;
*/ */
final class Receipt extends Entity final class Receipt extends Entity
{ {
/**
* Тип документа
*/
public const DOC_TYPE = 'receipt';
/** /**
* @var Client Покупатель * @var Client Покупатель
*/ */
@ -360,6 +374,86 @@ final class Receipt extends Entity
return $this; return $this;
} }
/**
* Регистрирует приход по текущему документу
*
* @param KktFiscalizer $fiscalizer Объект фискализатора
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse|null
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/
public function sell(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse
{
return $fiscalizer->sell($this, $external_id);
}
/**
* Регистрирует возврат прихода по текущему документу
*
* @param KktFiscalizer $fiscalizer Объект фискализатора
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse|null
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/
public function sellRefund(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse
{
return $fiscalizer->sellRefund($this, $external_id);
}
/**
* Регистрирует расход по текущему документу
*
* @param KktFiscalizer $fiscalizer Объект фискализатора
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse|null
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/
public function buy(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse
{
return $fiscalizer->buy($this, $external_id);
}
/**
* Регистрирует возврат расхода по текущему документу
*
* @param KktFiscalizer $fiscalizer Объект фискализатора
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
* @return KktResponse|null
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws TooLongPaymentAddressException
*/
public function buyRefund(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse
{
return $fiscalizer->buyRefund($this, $external_id);
}
/** /**
* Возвращает массив для кодирования в json * Возвращает массив для кодирования в json
* *
@ -368,18 +462,19 @@ final class Receipt extends Entity
public function jsonSerialize(): array public function jsonSerialize(): array
{ {
$json = [ $json = [
'client' => $this->getClient(), 'client' => $this->getClient()->jsonSerialize(),
'company' => $this->getCompany(), 'company' => $this->getCompany()->jsonSerialize(),
'items' => $this->getItems()->jsonSerialize(), 'items' => $this->getItems()->jsonSerialize(),
'total' => $this->getTotal(), 'total' => $this->getTotal(),
'payments' => $this->getPayments()->jsonSerialize(), 'payments' => $this->getPayments()->jsonSerialize(),
]; ];
$this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo(); $this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo()->jsonSerialize();
$this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier(); $this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier()->jsonSerialize();
$this->getVats()?->isNotEmpty() && $json['vats'] = $this->getVats(); $this->getVats()?->isNotEmpty() && $json['vats'] = $this->getVats();
!is_null($this->getAddCheckProps()) && $json['additional_check_props'] = $this->getAddCheckProps(); !is_null($this->getAddCheckProps()) && $json['additional_check_props'] = $this->getAddCheckProps();
!is_null($this->getCashier()) && $json['cashier'] = $this->getCashier(); !is_null($this->getCashier()) && $json['cashier'] = $this->getCashier();
$this->getAddUserProps()?->jsonSerialize() && $json['additional_user_props'] = $this->getAddUserProps(); $this->getAddUserProps()?->jsonSerialize() &&
$json['additional_user_props'] = $this->getAddUserProps()->jsonSerialize();
return $json; return $json;
} }
} }

View File

@ -1,36 +0,0 @@
<?php
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
declare(strict_types = 1);
namespace AtolOnline\Enums;
/**
* Константы, определяющие типы документов
*/
final class DocumentTypes extends Enum
{
/**
* Документ прихода, возврата прихода, расхода, возврата расхода
*/
const RECEIPT = 'receipt';
/**
* Документ коррекции
*/
const CORRECTION = 'correction';
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [];
}
}

View File

@ -0,0 +1,20 @@
<?php
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
declare(strict_types = 1);
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать пустую группу ККТ
*/
class EmptyGroupException extends AtolException
{
protected $message = 'Группа ККТ должна быть указана';
}

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать некорректную дату коррекции * Исключение, возникающее при попытке указать некорректную дату коррекции
@ -26,6 +27,7 @@ class InvalidCorrectionDateException extends AtolException
* @param string $date * @param string $date
* @param string $message * @param string $message
*/ */
#[Pure]
public function __construct(string $date = '', string $message = '') public function __construct(string $date = '', string $message = '')
{ {
parent::__construct("Ошибка даты документа коррекции '$date': " . $message); parent::__construct("Ошибка даты документа коррекции '$date': " . $message);

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при ошибке валидации кода таможенной декларации * Исключение, возникающее при ошибке валидации кода таможенной декларации
@ -25,6 +26,7 @@ class InvalidDeclarationNumberException extends AtolException
* *
* @param string $code * @param string $code
*/ */
#[Pure]
public function __construct(string $code = '') public function __construct(string $code = '')
{ {
parent::__construct("Невалидный код таможенной декларации: '$code'"); parent::__construct("Невалидный код таможенной декларации: '$code'");

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при ошибке валидации email * Исключение, возникающее при ошибке валидации email
@ -30,6 +31,7 @@ class InvalidEmailException extends AtolException
* *
* @param string $email * @param string $email
*/ */
#[Pure]
public function __construct(string $email = '') public function __construct(string $email = '')
{ {
parent::__construct("Невалидный email: '$email'"); parent::__construct("Невалидный email: '$email'");

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать ИНН некорректной длины * Исключение, возникающее при попытке указать ИНН некорректной длины
@ -31,6 +32,7 @@ class InvalidInnLengthException extends AtolException
* @param string $inn * @param string $inn
* @param string $message * @param string $message
*/ */
#[Pure]
public function __construct(string $inn = '', string $message = '') public function __construct(string $inn = '', string $message = '')
{ {
parent::__construct( parent::__construct(

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при работе с невалидным JSON * Исключение, возникающее при работе с невалидным JSON
*/ */
@ -19,6 +21,7 @@ class InvalidJsonException extends AtolException
/** /**
* Конструктор * Конструктор
*/ */
#[Pure]
public function __construct() public function __construct()
{ {
parent::__construct('[' . json_last_error() . '] ' . json_last_error_msg()); parent::__construct('[' . json_last_error() . '] ' . json_last_error_msg());

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при ошибке валидации кода страны происхождения товара * Исключение, возникающее при ошибке валидации кода страны происхождения товара
@ -27,6 +28,7 @@ class InvalidOKSMCodeException extends AtolException
* *
* @param string $code * @param string $code
*/ */
#[Pure]
public function __construct(string $code) public function __construct(string $code)
{ {
parent::__construct('Невалидный код страны происхождения товара: ' . $code); parent::__construct('Невалидный код страны происхождения товара: ' . $code);

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при ошибке валидации номера телефона * Исключение, возникающее при ошибке валидации номера телефона
@ -31,6 +32,7 @@ class InvalidPhoneException extends AtolException
* *
* @param string $phone * @param string $phone
*/ */
#[Pure]
public function __construct(string $phone = '') public function __construct(string $phone = '')
{ {
parent::__construct("Невалидный номер телефона: '$phone'"); parent::__construct("Невалидный номер телефона: '$phone'");

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать предмету расчёта отрицательный акциз * Исключение, возникающее при попытке указать предмету расчёта отрицательный акциз
@ -26,6 +27,7 @@ class NegativeItemExciseException extends AtolException
* @param string $name * @param string $name
* @param float $excise * @param float $excise
*/ */
#[Pure]
public function __construct(string $name, float $excise) public function __construct(string $name, float $excise)
{ {
parent::__construct("Предмет расчёта '$name' не может иметь отрицательный акциз $excise"); parent::__construct("Предмет расчёта '$name' не может иметь отрицательный акциз $excise");

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать предмету расчёта отрицательную цену * Исключение, возникающее при попытке указать предмету расчёта отрицательную цену
@ -26,6 +27,7 @@ class NegativeItemPriceException extends AtolException
* @param string $name * @param string $name
* @param float $price * @param float $price
*/ */
#[Pure]
public function __construct(string $name, float $price) public function __construct(string $name, float $price)
{ {
parent::__construct("Предмет расчёта '$name' не может иметь отрицательную цену $price"); parent::__construct("Предмет расчёта '$name' не может иметь отрицательную цену $price");

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать предмету расчёта отрицательное количество * Исключение, возникающее при попытке указать предмету расчёта отрицательное количество
@ -26,6 +27,7 @@ class NegativeItemQuantityException extends AtolException
* @param string $name * @param string $name
* @param float $quantity * @param float $quantity
*/ */
#[Pure]
public function __construct(string $name, float $quantity) public function __construct(string $name, float $quantity)
{ {
parent::__construct("Предмет расчёта '$name' не может иметь отрицательное количество $quantity"); parent::__construct("Предмет расчёта '$name' не может иметь отрицательное количество $quantity");

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать оплате отрицательную сумму * Исключение, возникающее при попытке указать оплате отрицательную сумму
@ -31,6 +32,7 @@ class NegativePaymentSumException extends AtolException
* *
* @param float $sum * @param float $sum
*/ */
#[Pure]
public function __construct(float $sum) public function __construct(float $sum)
{ {
parent::__construct('Размер оплаты не может быть отрицательным: ' . $sum); parent::__construct('Размер оплаты не может быть отрицательным: ' . $sum);

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке создать объект ККТ с неполными данными от монитора * Исключение, возникающее при попытке создать объект ККТ с неполными данными от монитора
*/ */
@ -27,6 +29,7 @@ class NotEnoughMonitorDataException extends AtolException
* @param array $props_diff * @param array $props_diff
* @param string $message * @param string $message
*/ */
#[Pure]
public function __construct(array $props_diff, string $message = '') public function __construct(array $props_diff, string $message = '')
{ {
parent::__construct($message ?: $this->message . implode(', ', $props_diff)); parent::__construct($message ?: $this->message . implode(', ', $props_diff));

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать слишком высокую цену (сумму) предмета расчёта * Исключение, возникающее при попытке указать слишком высокую цену (сумму) предмета расчёта
@ -28,6 +29,7 @@ class TooHighItemPriceException extends TooManyException
* @param string $name * @param string $name
* @param float $price * @param float $price
*/ */
#[Pure]
public function __construct(string $name, float $price) public function __construct(string $name, float $price)
{ {
parent::__construct($price, "Слишком высокая цена для предмета расчёта '$name'"); parent::__construct($price, "Слишком высокая цена для предмета расчёта '$name'");

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке добавить слишком большое количество предмета расчёта * Исключение, возникающее при попытке добавить слишком большое количество предмета расчёта
@ -28,6 +29,7 @@ class TooHighItemQuantityException extends TooManyException
* @param string $name * @param string $name
* @param float $quantity * @param float $quantity
*/ */
#[Pure]
public function __construct(string $name, float $quantity) public function __construct(string $name, float $quantity)
{ {
parent::__construct($quantity, "Слишком большое количество предмета расчёта '$name'"); parent::__construct($quantity, "Слишком большое количество предмета расчёта '$name'");

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке получеиня слишком высокой стоимости предмета расчёта * Исключение, возникающее при попытке получеиня слишком высокой стоимости предмета расчёта
@ -28,6 +29,7 @@ class TooHighItemSumException extends TooManyException
* @param string $name * @param string $name
* @param float $sum * @param float $sum
*/ */
#[Pure]
public function __construct(string $name, float $sum) public function __construct(string $name, float $sum)
{ {
parent::__construct($sum, "Слишком высокая стоимость предмета расчёта '$name'"); parent::__construct($sum, "Слишком высокая стоимость предмета расчёта '$name'");

View File

@ -18,6 +18,6 @@ use AtolOnline\Constants\Constraints;
*/ */
class TooLongCallbackUrlException extends TooLongException class TooLongCallbackUrlException extends TooLongException
{ {
protected $message = 'Слишком длинный адрес колбека'; protected $message = 'Слишком длинный callback_url';
protected float $max = Constraints::MAX_LENGTH_CALLBACK_URL; protected float $max = Constraints::MAX_LENGTH_CALLBACK_URL;
} }

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать слишком длинное что-либо * Исключение, возникающее при попытке указать слишком длинное что-либо
*/ */
@ -33,6 +35,7 @@ class TooLongException extends AtolException
* @param string $message * @param string $message
* @param float $max * @param float $max
*/ */
#[Pure]
public function __construct(string $value, string $message = '', float $max = 0) public function __construct(string $value, string $message = '', float $max = 0)
{ {
parent::__construct( parent::__construct(

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags; use AtolOnline\Constants\Ffd105Tags;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать слишком длинный код товара * Исключение, возникающее при попытке указать слишком длинный код товара
@ -28,6 +29,7 @@ class TooLongItemCodeException extends TooLongException
* @param string $name * @param string $name
* @param string $code * @param string $code
*/ */
#[Pure]
public function __construct(string $name, string $code) public function __construct(string $name, string $code)
{ {
parent::__construct($code, "Слишком длинный код товара '$name'"); parent::__construct($code, "Слишком длинный код товара '$name'");

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions; namespace AtolOnline\Exceptions;
use JetBrains\PhpStorm\Pure;
/** /**
* Исключение, возникающее при попытке указать слишком большое количество чего-либо * Исключение, возникающее при попытке указать слишком большое количество чего-либо
*/ */
@ -33,6 +35,7 @@ class TooManyException extends AtolException
* @param string $message * @param string $message
* @param float|null $max * @param float|null $max
*/ */
#[Pure]
public function __construct(float $value, string $message = '', ?float $max = null) public function __construct(float $value, string $message = '', ?float $max = null)
{ {
parent::__construct( parent::__construct(

View File

@ -0,0 +1,417 @@
<?php
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Tests\Api;
use AtolOnline\{
Constants\Constraints,
Helpers,
TestEnvParams,
Tests\BasicTestCase};
use AtolOnline\Api\{
AtolClient,
KktFiscalizer};
use AtolOnline\Exceptions\{
AuthFailedException,
EmptyCorrectionNumberException,
EmptyGroupException,
EmptyItemNameException,
EmptyItemsException,
EmptyLoginException,
EmptyPasswordException,
InvalidCallbackUrlException,
InvalidCorrectionDateException,
InvalidEntityInCollectionException,
InvalidEnumValueException,
InvalidInnLengthException,
InvalidPaymentAddressException,
InvalidUuidException,
NegativeItemPriceException,
NegativeItemQuantityException,
NegativePaymentSumException,
TooHighItemPriceException,
TooHighPaymentSumException,
TooLongCallbackUrlException,
TooLongItemNameException,
TooLongLoginException,
TooLongPasswordException,
TooLongPaymentAddressException,
TooManyException};
use GuzzleHttp\Exception\GuzzleException;
/**
* Набор тестов для проверки работы фискализатора
*/
class KktFiscalizerTest extends BasicTestCase
{
/**
* @var array Массив UUID-ов результатов регистрации документов для переиспользования
* в тестах получения их статусов фискализации
*/
private static array $registered_uuids = [];
/**
* Тестирует успешное создание объекта фискализатора без аргументов конструктора
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer
*/
public function testConstructorWithourArgs(): void
{
$fisc = new KktFiscalizer();
$this->assertIsObject($fisc);
$this->assertIsSameClass(KktFiscalizer::class, $fisc);
$this->assertExtendsClasses([AtolClient::class], $fisc);
}
/**
* Тестирует установку и возврат группы ККТ
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer
* @covers \AtolOnline\Api\KktFiscalizer::getGroup
* @covers \AtolOnline\Api\KktFiscalizer::setGroup
*/
public function testGroup(): void
{
// test mode
$this->assertEquals(
TestEnvParams::FFD105()['group'],
(new KktFiscalizer(group: 'group'))->getGroup()
);
// prod mode
$this->assertEquals('group', (new KktFiscalizer(false, group: 'group'))->getGroup());
$this->assertNull((new KktFiscalizer(false))->getGroup());
}
/**
* Тестирует выброс исключения при попытке передать пустую группу ККТ в конструктор
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer
* @covers \AtolOnline\Api\KktFiscalizer::setGroup
* @covers \AtolOnline\Exceptions\EmptyGroupException
*/
public function testEmptyGroupException(): void
{
$this->expectException(EmptyGroupException::class);
new KktFiscalizer(group: "\n\r \0\t");
}
/**
* Тестирует выброс исключения при попытке установить слишком длинный адрес колбека
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl
* @covers \AtolOnline\Exceptions\TooLongCallbackUrlException
* @throws InvalidCallbackUrlException
* @throws TooLongCallbackUrlException
*/
public function testTooLongCallbackUrlException(): void
{
$this->expectException(TooLongCallbackUrlException::class);
(new KktFiscalizer())->setCallbackUrl(Helpers::randomStr(Constraints::MAX_LENGTH_CALLBACK_URL + 1));
}
/**
* Тестирует выброс исключения при попытке установить слишком длинный адрес колбека
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl
* @covers \AtolOnline\Exceptions\InvalidCallbackUrlException
* @throws InvalidCallbackUrlException
* @throws TooLongCallbackUrlException
*/
public function testInvalidCallbackUrlException(): void
{
$this->expectException(InvalidCallbackUrlException::class);
(new KktFiscalizer())->setCallbackUrl(Helpers::randomStr());
}
/**
* Тестирует обнуление адреса колбека
*
* @param mixed $param
* @return void
* @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl
* @covers \AtolOnline\Api\KktFiscalizer::getCallbackUrl
* @dataProvider providerNullableStrings
* @throws InvalidCallbackUrlException
* @throws TooLongCallbackUrlException
*/
public function testNullableCallbackUrl(mixed $param): void
{
$this->assertNull((new KktFiscalizer())->setCallbackUrl($param)->getCallbackUrl());
}
/**
* Тестирует регистрацию документа прихода
*
* @return void
* @covers \AtolOnline\Entities\Receipt::sell
* @covers \AtolOnline\Api\KktFiscalizer::sell
* @covers \AtolOnline\Api\KktFiscalizer::getFullUrl
* @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::registerDocument
* @throws AuthFailedException
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyException
* @throws GuzzleException
*/
public function testSell(): void
{
$fisc_result = $this->newReceipt()->sell(new KktFiscalizer());
$this->assertTrue($fisc_result->isValid());
$this->assertEquals('wait', $fisc_result->getContent()->status);
self::$registered_uuids[] = $fisc_result->getContent()->uuid;
}
/**
* Тестирует регистрацию документа возврата прихода
*
* @return void
* @covers \AtolOnline\Entities\Receipt::sellRefund
* @covers \AtolOnline\Api\KktFiscalizer::sellRefund
* @covers \AtolOnline\Api\KktFiscalizer::getFullUrl
* @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::registerDocument
* @throws AuthFailedException
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyException
* @throws GuzzleException
*/
public function testSellRefund(): void
{
$fisc_result = $this->newReceipt()->sellRefund(new KktFiscalizer());
$this->assertTrue($fisc_result->isValid());
$this->assertEquals('wait', $fisc_result->getContent()->status);
self::$registered_uuids[] = $fisc_result->getContent()->uuid;
}
/**
* Тестирует регистрацию документа возврата прихода
*
* @return void
* @covers \AtolOnline\Entities\Correction::sellCorrect
* @covers \AtolOnline\Api\KktFiscalizer::sellCorrect
* @covers \AtolOnline\Api\KktFiscalizer::getFullUrl
* @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::registerDocument
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws NegativePaymentSumException
* @throws TooHighPaymentSumException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @throws TooLongPaymentAddressException
* @throws EmptyCorrectionNumberException
* @throws InvalidCorrectionDateException
*/
public function testSellCorrect(): void
{
$fisc_result = $this->newCorrection()->sellCorrect(new KktFiscalizer());
$this->assertTrue($fisc_result->isValid());
$this->assertEquals('wait', $fisc_result->getContent()->status);
self::$registered_uuids[] = $fisc_result->getContent()->uuid;
}
/**
* Тестирует регистрацию документа расхода
*
* @return void
* @covers \AtolOnline\Entities\Receipt::buy
* @covers \AtolOnline\Api\KktFiscalizer::buy
* @covers \AtolOnline\Api\KktFiscalizer::getFullUrl
* @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::registerDocument
* @throws AuthFailedException
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyException
* @throws GuzzleException
*/
public function testBuy(): void
{
$fisc_result = $this->newReceipt()->buy(new KktFiscalizer());
$this->assertTrue($fisc_result->isValid());
$this->assertEquals('wait', $fisc_result->getContent()->status);
self::$registered_uuids[] = $fisc_result->getContent()->uuid;
}
/**
* Тестирует регистрацию документа возврата расхода
*
* @return void
* @covers \AtolOnline\Entities\Receipt::buyRefund
* @covers \AtolOnline\Api\KktFiscalizer::buyRefund
* @covers \AtolOnline\Api\KktFiscalizer::getFullUrl
* @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::registerDocument
* @throws AuthFailedException
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @throws TooLongPaymentAddressException
* @throws TooManyException
* @throws GuzzleException
*/
public function testBuyRefund(): void
{
$fisc_result = $this->newReceipt()->buyRefund(new KktFiscalizer());
$this->assertTrue($fisc_result->isValid());
$this->assertEquals('wait', $fisc_result->getContent()->status);
self::$registered_uuids[] = $fisc_result->getContent()->uuid;
}
/**
* Тестирует регистрацию документа возврата прихода
*
* @return void
* @covers \AtolOnline\Entities\Correction::buyCorrect
* @covers \AtolOnline\Api\KktFiscalizer::buyCorrect
* @covers \AtolOnline\Api\KktFiscalizer::getFullUrl
* @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint
* @covers \AtolOnline\Api\KktFiscalizer::registerDocument
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws NegativePaymentSumException
* @throws TooHighPaymentSumException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @throws TooLongPaymentAddressException
* @throws EmptyCorrectionNumberException
* @throws InvalidCorrectionDateException
*/
public function testBuyCorrect(): void
{
$fisc_result = $this->newCorrection()->buyCorrect(new KktFiscalizer());
$this->assertTrue($fisc_result->isValid());
$this->assertEquals('wait', $fisc_result->getContent()->status);
self::$registered_uuids[] = $fisc_result->getContent()->uuid;
}
/**
* Тестирует разовое получение статуса фискализации документа
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer::getDocumentStatus
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidUuidException
* @throws TooLongLoginException
* @throws TooLongPasswordException
*/
public function testGetDocumentStatus(): void
{
$fisc_status = (new KktFiscalizer())->getDocumentStatus(self::$registered_uuids[0]);
$this->assertTrue($fisc_status->isValid());
$this->assertTrue(in_array($fisc_status->getContent()->status, ['wait', 'done']));
}
/**
* Тестирует опрос API на получение статуса фискализации документа
*
* @return void
* @covers \AtolOnline\Api\KktFiscalizer::pollDocumentStatus
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidUuidException
* @throws TooLongLoginException
* @throws TooLongPasswordException
*/
public function testPollDocumentStatus(): void
{
$fisc_status = (new KktFiscalizer())->pollDocumentStatus(self::$registered_uuids[1]);
$this->assertTrue($fisc_status->isValid());
$this->assertEquals('done', $fisc_status->getContent()->status);
}
}

View File

@ -92,14 +92,14 @@ class KktMonitorTest extends BasicTestCase
*/ */
public function testLogin(): void public function testLogin(): void
{ {
$client = new KktMonitor(login: 'login'); $client = new KktMonitor(false, login: 'login');
$this->assertEquals('login', $client->getLogin()); $this->assertEquals('login', $client->getLogin());
$client = new KktMonitor(); $client = new KktMonitor();
$this->assertNull($client->getLogin()); $this->assertEquals(TestEnvParams::FFD105()['login'], $client->getLogin());
$client->setLogin('login'); $client->setLogin('login');
$this->assertEquals('login', $client->getLogin()); $this->assertEquals(TestEnvParams::FFD105()['login'], $client->getLogin());
} }
/** /**
@ -143,14 +143,14 @@ class KktMonitorTest extends BasicTestCase
*/ */
public function testPassword(): void public function testPassword(): void
{ {
$client = new KktMonitor(password: 'password'); $client = new KktMonitor(false, password: 'password');
$this->assertEquals('password', $client->getPassword()); $this->assertEquals('password', $client->getPassword());
$client = new KktMonitor(); $client = new KktMonitor();
$this->assertNull($client->getPassword()); $this->assertEquals(TestEnvParams::FFD105()['password'], $client->getPassword());
$client->setPassword('password'); $client->setPassword('password');
$this->assertEquals('password', $client->getPassword()); $this->assertEquals(TestEnvParams::FFD105()['password'], $client->getPassword());
} }
/** /**
@ -262,7 +262,7 @@ class KktMonitorTest extends BasicTestCase
* Тестирует возврат объекта последнего ответа от API * Тестирует возврат объекта последнего ответа от API
* *
* @depends testAuth * @depends testAuth
* @covers \AtolOnline\Api\KktMonitor::getResponse * @covers \AtolOnline\Api\KktMonitor::getLastResponse
* @covers \AtolOnline\Exceptions\AuthFailedException * @covers \AtolOnline\Exceptions\AuthFailedException
* @throws AuthFailedException * @throws AuthFailedException
* @throws EmptyLoginException * @throws EmptyLoginException
@ -276,7 +276,7 @@ class KktMonitorTest extends BasicTestCase
$this->skipIfMonitoringIsOffline(); $this->skipIfMonitoringIsOffline();
$client = $this->newTestClient(); $client = $this->newTestClient();
$client->auth(); $client->auth();
$this->assertIsSameClass(KktResponse::class, $client->getResponse()); $this->assertIsSameClass(KktResponse::class, $client->getLastResponse());
} }
/** /**
@ -301,7 +301,8 @@ class KktMonitorTest extends BasicTestCase
$client = $this->newTestClient(); $client = $this->newTestClient();
$client->auth(); $client->auth();
$kkts = $client->getAll(); $kkts = $client->getAll();
$this->assertNotEmpty($client->getResponse()->getContent()); $sss = $kkts->where('deviceNumber', 'KKT014034');
$this->assertNotEmpty($client->getLastResponse()->getContent());
$this->assertIsCollection($kkts); $this->assertIsCollection($kkts);
$this->assertTrue($kkts->count() > 0); $this->assertTrue($kkts->count() > 0);
$this->assertIsSameClass(Kkt::class, $kkts->random()); $this->assertIsSameClass(Kkt::class, $kkts->random());
@ -336,7 +337,7 @@ class KktMonitorTest extends BasicTestCase
$client->auth(); $client->auth();
$serial_number = $client->getAll()->first()->serialNumber; $serial_number = $client->getAll()->first()->serialNumber;
$kkt = $client->getOne($serial_number); $kkt = $client->getOne($serial_number);
$this->assertNotEmpty($client->getResponse()); $this->assertNotEmpty($client->getLastResponse());
$this->assertIsSameClass(Kkt::class, $kkt); $this->assertIsSameClass(Kkt::class, $kkt);
$this->assertIsAtolable($kkt); $this->assertIsAtolable($kkt);
$this->assertNotNull($kkt->serialNumber); $this->assertNotNull($kkt->serialNumber);

View File

@ -12,13 +12,27 @@ declare(strict_types = 1);
namespace AtolOnline\Tests; namespace AtolOnline\Tests;
use AtolOnline\Collections\EntityCollection; use AtolOnline\Collections\EntityCollection;
use AtolOnline\Collections\Items;
use AtolOnline\Collections\Payments;
use AtolOnline\Collections\Vats;
use AtolOnline\Entities\Client;
use AtolOnline\Entities\Company;
use AtolOnline\Entities\Correction;
use AtolOnline\Entities\CorrectionInfo;
use AtolOnline\Entities\Entity; use AtolOnline\Entities\Entity;
use AtolOnline\Entities\Item; use AtolOnline\Entities\Item;
use AtolOnline\Entities\Payment; use AtolOnline\Entities\Payment;
use AtolOnline\Entities\Receipt;
use AtolOnline\Entities\Vat; use AtolOnline\Entities\Vat;
use AtolOnline\Enums\CorrectionTypes;
use AtolOnline\Enums\PaymentTypes; use AtolOnline\Enums\PaymentTypes;
use AtolOnline\Enums\SnoTypes;
use AtolOnline\Enums\VatTypes; use AtolOnline\Enums\VatTypes;
use AtolOnline\Exceptions\EmptyCorrectionNumberException;
use AtolOnline\Exceptions\EmptyItemNameException; use AtolOnline\Exceptions\EmptyItemNameException;
use AtolOnline\Exceptions\EmptyItemsException;
use AtolOnline\Exceptions\InvalidCorrectionDateException;
use AtolOnline\Exceptions\InvalidEntityInCollectionException;
use AtolOnline\Exceptions\InvalidEnumValueException; use AtolOnline\Exceptions\InvalidEnumValueException;
use AtolOnline\Exceptions\NegativeItemPriceException; use AtolOnline\Exceptions\NegativeItemPriceException;
use AtolOnline\Exceptions\NegativeItemQuantityException; use AtolOnline\Exceptions\NegativeItemQuantityException;
@ -29,7 +43,7 @@ use AtolOnline\Exceptions\TooLongItemNameException;
use AtolOnline\Exceptions\TooManyException; use AtolOnline\Exceptions\TooManyException;
use AtolOnline\Helpers; use AtolOnline\Helpers;
use Exception; use Exception;
use GuzzleHttp\Client; use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -53,7 +67,7 @@ class BasicTestCase extends TestCase
protected function ping(string $url, int $code): bool protected function ping(string $url, int $code): bool
{ {
try { try {
$result = (new Client([ $result = (new GuzzleClient([
'http_errors' => false, 'http_errors' => false,
'timeout' => 3, 'timeout' => 3,
]))->request('GET', $url); ]))->request('GET', $url);
@ -387,4 +401,51 @@ class BasicTestCase extends TestCase
} }
return $result; return $result;
} }
/**
* Возвращает валидный тестовый объект чека прихода
*
* @return Receipt
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooManyException
*/
protected function newReceipt(): Receipt
{
return new Receipt(
new Client('John Doe', 'john@example.com', '+79501234567', '1234567890'),
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
new Items($this->generateItemObjects(2)),
new Payments($this->generatePaymentObjects())
);
}
/**
* Возвращает валидный тестовый объект чека
*
* @return Correction
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws NegativePaymentSumException
* @throws TooHighPaymentSumException
* @throws EmptyCorrectionNumberException
* @throws InvalidCorrectionDateException
*/
protected function newCorrection(): Correction
{
return new Correction(
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', Helpers::randomStr()),
new Payments($this->generatePaymentObjects(2)),
new Vats($this->generateVatObjects(2)),
);
}
} }

View File

@ -9,18 +9,18 @@
namespace AtolOnline\Tests\Entities; namespace AtolOnline\Tests\Entities;
use AtolOnline\{Constants\Constraints, Helpers, Tests\BasicTestCase}; use AtolOnline\{
use AtolOnline\Collections\{Payments, Vats,}; Constants\Constraints,
use AtolOnline\Entities\{Company, Correction, CorrectionInfo}; Helpers,
use AtolOnline\Enums\{CorrectionTypes, SnoTypes}; Tests\BasicTestCase};
use AtolOnline\Exceptions\{EmptyCorrectionNumberException, use AtolOnline\Exceptions\{
EmptyCorrectionNumberException,
InvalidCorrectionDateException, InvalidCorrectionDateException,
InvalidEntityInCollectionException, InvalidEntityInCollectionException,
InvalidEnumValueException, InvalidEnumValueException,
NegativePaymentSumException, NegativePaymentSumException,
TooHighPaymentSumException, TooHighPaymentSumException,
TooLongCashierException TooLongCashierException};
};
use Exception; use Exception;
/** /**
@ -114,25 +114,4 @@ class CorrectionTest extends BasicTestCase
$this->expectException(TooLongCashierException::class); $this->expectException(TooLongCashierException::class);
$this->newCorrection()->setCashier(Helpers::randomStr(Constraints::MAX_LENGTH_CASHIER_NAME + 1)); $this->newCorrection()->setCashier(Helpers::randomStr(Constraints::MAX_LENGTH_CASHIER_NAME + 1));
} }
/**
* Возвращает валидный тестовый объект чека
*
* @return Correction
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws NegativePaymentSumException
* @throws TooHighPaymentSumException
* @throws EmptyCorrectionNumberException
* @throws InvalidCorrectionDateException
*/
protected function newCorrection(): Correction
{
return new Correction(
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', Helpers::randomStr()),
new Payments($this->generatePaymentObjects(2)),
new Vats($this->generateVatObjects(2)),
);
}
} }

View File

@ -127,7 +127,7 @@ class ReceiptTest extends BasicTestCase
); );
$receipt = $this->newReceipt()->setAgentInfo($agent_info); $receipt = $this->newReceipt()->setAgentInfo($agent_info);
$this->assertArrayHasKey('agent_info', $receipt->jsonSerialize()); $this->assertArrayHasKey('agent_info', $receipt->jsonSerialize());
$this->assertEquals($receipt->getAgentInfo(), $receipt->jsonSerialize()['agent_info']); $this->assertEquals($receipt->getAgentInfo()->jsonSerialize(), $receipt->jsonSerialize()['agent_info']);
$this->assertArrayNotHasKey('agent_info', $receipt->setAgentInfo(null)->jsonSerialize()); $this->assertArrayNotHasKey('agent_info', $receipt->setAgentInfo(null)->jsonSerialize());
} }
@ -159,7 +159,7 @@ class ReceiptTest extends BasicTestCase
$supplier = new Supplier('some name', '+fasd3\qe3fs_=nac99013928czc', ['+122997365456']); $supplier = new Supplier('some name', '+fasd3\qe3fs_=nac99013928czc', ['+122997365456']);
$receipt = $this->newReceipt()->setSupplier($supplier); $receipt = $this->newReceipt()->setSupplier($supplier);
$this->assertArrayHasKey('supplier_info', $receipt->jsonSerialize()); $this->assertArrayHasKey('supplier_info', $receipt->jsonSerialize());
$this->assertEquals($receipt->getSupplier(), $receipt->jsonSerialize()['supplier_info']); $this->assertEquals($receipt->getSupplier()->jsonSerialize(), $receipt->jsonSerialize()['supplier_info']);
$this->assertArrayNotHasKey('supplier_info', $receipt->setSupplier(null)->jsonSerialize()); $this->assertArrayNotHasKey('supplier_info', $receipt->setSupplier(null)->jsonSerialize());
} }
@ -207,7 +207,9 @@ class ReceiptTest extends BasicTestCase
public function testInvalidItemInCollectionException(): void public function testInvalidItemInCollectionException(): void
{ {
$this->expectException(InvalidEntityInCollectionException::class); $this->expectException(InvalidEntityInCollectionException::class);
$this->expectErrorMessage('Коллекция AtolOnline\Collections\Items должна содержать объекты AtolOnline\Entities\Item'); $this->expectErrorMessage(
'Коллекция AtolOnline\Collections\Items должна содержать объекты AtolOnline\Entities\Item'
);
new Receipt( new Receipt(
new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'),
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
@ -267,7 +269,9 @@ class ReceiptTest extends BasicTestCase
public function testInvalidPaymentInCollectionException(): void public function testInvalidPaymentInCollectionException(): void
{ {
$this->expectException(InvalidEntityInCollectionException::class); $this->expectException(InvalidEntityInCollectionException::class);
$this->expectErrorMessage('Коллекция AtolOnline\Collections\Payments должна содержать объекты AtolOnline\Entities\Payment'); $this->expectErrorMessage(
'Коллекция AtolOnline\Collections\Payments должна содержать объекты AtolOnline\Entities\Payment'
);
(string)new Receipt( (string)new Receipt(
new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'),
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
@ -330,7 +334,9 @@ class ReceiptTest extends BasicTestCase
public function testInvalidVatInCollectionException(): void public function testInvalidVatInCollectionException(): void
{ {
$this->expectException(InvalidEntityInCollectionException::class); $this->expectException(InvalidEntityInCollectionException::class);
$this->expectErrorMessage('Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat'); $this->expectErrorMessage(
'Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat'
);
(string)$this->newReceipt()->setVats(new Vats(['qwerty'])); (string)$this->newReceipt()->setVats(new Vats(['qwerty']));
} }
@ -357,15 +363,8 @@ class ReceiptTest extends BasicTestCase
*/ */
public function testCalculations(): void public function testCalculations(): void
{ {
$items_total = 0;
$receipt = $this->newReceipt(); $receipt = $this->newReceipt();
$items_total = $receipt->getItems()->pluck('sum')->sum();
//TODO при $receipt->getItems()->pluck('sum') стреляет InvalidEntityInCollectionException
// см. примечания в конструкторе EntityCollection
$receipt->getItems()->each(function ($item) use (&$items_total) {
/** @var Item $item */
return $items_total += $item->getSum();
});
$this->assertEquals($items_total, $receipt->getTotal()); $this->assertEquals($items_total, $receipt->getTotal());
/** @var Vat $vat */ /** @var Vat $vat */
@ -563,34 +562,10 @@ class ReceiptTest extends BasicTestCase
$aup = new AdditionalUserProps('name', 'value'); $aup = new AdditionalUserProps('name', 'value');
$receipt = $this->newReceipt()->setAddUserProps($aup); $receipt = $this->newReceipt()->setAddUserProps($aup);
$this->assertArrayHasKey('additional_user_props', $receipt->jsonSerialize()); $this->assertArrayHasKey('additional_user_props', $receipt->jsonSerialize());
$this->assertEquals($receipt->getAddUserProps(), $receipt->jsonSerialize()['additional_user_props']); $this->assertEquals(
$receipt->getAddUserProps()->jsonSerialize(),
$receipt->jsonSerialize()['additional_user_props']
);
$this->assertArrayNotHasKey('additional_user_props', $receipt->setAddUserProps(null)->jsonSerialize()); $this->assertArrayNotHasKey('additional_user_props', $receipt->setAddUserProps(null)->jsonSerialize());
} }
/**
* Возвращает валидный тестовый объект чека
*
* @return Receipt
* @throws EmptyItemNameException
* @throws EmptyItemsException
* @throws EmptyPaymentsException
* @throws InvalidEntityInCollectionException
* @throws InvalidEnumValueException
* @throws NegativeItemPriceException
* @throws NegativeItemQuantityException
* @throws NegativePaymentSumException
* @throws TooHighItemPriceException
* @throws TooHighPaymentSumException
* @throws TooLongItemNameException
* @throws TooManyException
*/
protected function newReceipt(): Receipt
{
return new Receipt(
new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'),
new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'),
new Items($this->generateItemObjects(2)),
new Payments($this->generatePaymentObjects())
);
}
} }