Compare commits

...

3 Commits

22 changed files with 187 additions and 77 deletions

View File

@ -12,8 +12,17 @@
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="All">
<directory suffix="Test.php">./tests</directory>
<testsuite name="Helpers">
<file>tests/AtolOnline/Tests/HelpersTest.php</file>
</testsuite>
<testsuite name="Entities">
<directory>tests/AtolOnline/Tests/Entities</directory>
</testsuite>
<testsuite name="Collections">
<directory>tests/AtolOnline/Tests/Collections</directory>
</testsuite>
<testsuite name="Api">
<directory>tests/AtolOnline/Tests/Api</directory>
</testsuite>
</testsuites>

View File

@ -11,15 +11,19 @@ declare(strict_types = 1);
namespace AtolOnline\Api;
use AtolOnline\{
Constants\Constraints,
Exceptions\AuthFailedException,
Exceptions\EmptyLoginException,
Exceptions\EmptyPasswordException,
Exceptions\TooLongLoginException,
Exceptions\TooLongPasswordException};
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\{
AuthFailedException,
EmptyLoginException,
EmptyPasswordException,
TooLongLoginException,
TooLongPasswordException
};
use GuzzleHttp\{
Client,
Exception\GuzzleException
};
use JetBrains\PhpStorm\Pure;
/**
* Класс для подключения АТОЛ Онлайн API
@ -74,10 +78,13 @@ abstract class AtolClient
?string $login = null,
?string $password = null,
array $config = []
) {
$this->http = new Client(array_merge($config, [
)
{
$this->http = new Client(
array_merge($config, [
'http_errors' => $config['http_errors'] ?? false,
]));
])
);
$this->setTestMode($test_mode);
!is_null($login) && $this->setLogin($login);
!is_null($password) && $this->setPassword($password);
@ -201,6 +208,7 @@ abstract class AtolClient
*
* @return array
*/
#[Pure]
private function getHeaders(): array
{
$headers['Content-type'] = 'application/json; charset=utf-8';

View File

@ -12,10 +12,13 @@ declare(strict_types = 1);
namespace AtolOnline\Api;
use AtolOnline\Entities\Kkt;
use AtolOnline\Exceptions\EmptyMonitorDataException;
use AtolOnline\Exceptions\NotEnoughMonitorDataException;
use AtolOnline\Exceptions\{
EmptyMonitorDataException,
NotEnoughMonitorDataException
};
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection;
use JetBrains\PhpStorm\Pure;
/**
* Класс для мониторинга ККТ
@ -27,6 +30,7 @@ class KktMonitor extends AtolClient
/**
* @inheritDoc
*/
#[Pure]
protected function getAuthEndpoint(): string
{
return $this->isTestMode()
@ -37,6 +41,7 @@ class KktMonitor extends AtolClient
/**
* @inheritDoc
*/
#[Pure]
protected function getMainEndpoint(): string
{
return $this->isTestMode()

View File

@ -7,13 +7,18 @@
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
/** @noinspection PhpMultipleClassDeclarationsInspection */
declare(strict_types = 1);
namespace AtolOnline\Api;
use JetBrains\PhpStorm\{
ArrayShape,
Pure
};
use JsonSerializable;
use Psr\Http\Message\ResponseInterface;
use stdClass;
use Stringable;
/**
@ -30,9 +35,9 @@ class KktResponse implements JsonSerializable, Stringable
protected int $code;
/**
* @var stdClass|array|null Содержимое ответа сервера
* @var object|array|null Содержимое ответа сервера
*/
protected stdClass|array|null $content;
protected object|array|null $content;
/**
* @var array Заголовки ответа
@ -67,6 +72,7 @@ class KktResponse implements JsonSerializable, Stringable
* @param $name
* @return mixed
*/
#[Pure]
public function __get($name): mixed
{
return $this->getContent()?->$name;
@ -97,6 +103,7 @@ class KktResponse implements JsonSerializable, Stringable
*
* @return bool
*/
#[Pure]
public function isValid(): bool
{
return !empty($this->getCode())
@ -116,6 +123,12 @@ class KktResponse implements JsonSerializable, Stringable
/**
* @inheritDoc
*/
#[ArrayShape([
'code' => 'int',
'headers' => 'array|\string[][]',
'body' => 'mixed',
]
)]
public function jsonSerialize(): array
{
return [

View File

@ -43,24 +43,28 @@ final class Constraints
/**
* Максимальная длина наименования покупателя (1227)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
*/
const MAX_LENGTH_CLIENT_NAME = 256;
/**
* Максимальная длина наименования предмета расчёта (1030)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
*/
const MAX_LENGTH_ITEM_NAME = 128;
/**
* Максимальная цена за единицу предмета расчёта (1079)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
*/
const MAX_COUNT_ITEM_PRICE = 42949672.95;
/**
* Максимальное количество (вес) единицы предмета расчёта (1023)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
*/
const MAX_COUNT_ITEM_QUANTITY = 99999.999;
@ -68,36 +72,42 @@ final class Constraints
/**
* Максимальная стоимость всех предметов расчёта в документе прихода, расхода,
* возврата прихода, возврата расхода (1043)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
*/
const MAX_COUNT_ITEM_SUM = 42949672.95;
/**
* Максимальная длина телефона или email покупателя (1008)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
*/
const MAX_LENGTH_CLIENT_CONTACT = 64;
/**
* Длина операции для платёжного агента (1044)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19
*/
const MAX_LENGTH_PAYING_AGENT_OPERATION = 24;
/**
* Максимальное количество предметов расчёта в документе прихода, расхода, возврата прихода, возврата расхода
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
*/
const MAX_COUNT_DOC_ITEMS = 100;
/**
* Максимальная длина единицы измерения предмета расчётов
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
*/
const MAX_LENGTH_MEASUREMENT_UNIT = 16;
/**
* Максимальная длина пользовательских данных для предмета расчётов (1191)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29
*/
const MAX_LENGTH_USER_DATA = 64;
@ -118,12 +128,14 @@ final class Constraints
/**
* Максимальное количество платежей в любом документе
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30 и 35
*/
const MAX_COUNT_DOC_PAYMENTS = 10;
/**
* Максимальное количество ставок НДС в любом документе
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 31 и 36
*/
const MAX_COUNT_DOC_VATS = 6;
@ -178,26 +190,31 @@ final class Constraints
*
* @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/client/inn"
*/
const PATTERN_INN = /* @lang PhpRegExp */
const PATTERN_INN
= /* @lang PhpRegExp */
'/(^[\d]{10}$)|(^[\d]{12}$)/';
/**
* Регулярное выражение для валидации номера телефона
*
* @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/definitions/phone_number"
*/
const PATTERN_PHONE = /* @lang PhpRegExp */
const PATTERN_PHONE
= /* @lang PhpRegExp */
'/^([^\s\\\]{0,17}|\+[^\s\\\]{1,18})$/';
/**
* Регулярное выражение для валидации строки Callback URL
*/
const PATTERN_CALLBACK_URL = /* @lang PhpRegExp */
'/^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]' .
'([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/';
const PATTERN_CALLBACK_URL
= /* @lang PhpRegExp */
'/^http(s?):\/\/[0-9a-zA-Zа-яА-Я]' .
'([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-.?,\'\/\\\+&=%\$#_]*)?$/';
/**
* Регулярное выражение для валидации кода страны происхождения товара
*/
const PATTERN_OKSM_CODE = /* @lang PhpRegExp */
const PATTERN_OKSM_CODE
= /* @lang PhpRegExp */
'/^[\d]{3}$/';
}

View File

@ -16,8 +16,12 @@ use AtolOnline\Exceptions\{
EmptyAddUserPropNameException,
EmptyAddUserPropValueException,
TooLongAddUserPropNameException,
TooLongAddUserPropValueException};
use JetBrains\PhpStorm\Pure;
TooLongAddUserPropValueException
};
use JetBrains\PhpStorm\{
ArrayShape,
Pure
};
/**
* Класс, описывающий дополнительный реквизит пользователя
@ -117,6 +121,7 @@ final class AdditionalUserProps extends Entity
* @inheritDoc
*/
#[Pure]
#[ArrayShape(['name' => 'string', 'value' => 'null|string'])]
public function jsonSerialize(): array
{
return [

View File

@ -11,8 +11,14 @@ declare(strict_types = 1);
namespace AtolOnline\Entities;
use AtolOnline\{Constants\Constraints, Enums\SnoTypes, Traits\HasEmail, Traits\HasInn};
use AtolOnline\Exceptions\{InvalidEmailException,
use AtolOnline\{
Constants\Constraints,
Enums\SnoTypes,
Traits\HasEmail,
Traits\HasInn
};
use AtolOnline\Exceptions\{
InvalidEmailException,
InvalidEnumValueException,
InvalidInnLengthException,
InvalidPaymentAddressException,

View File

@ -9,9 +9,13 @@
namespace AtolOnline\Entities;
use AtolOnline\Collections\{Payments, Vats};
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\{InvalidEntityInCollectionException, TooLongCashierException};
use AtolOnline\{
Collections\Payments,
Collections\Vats,
Constants\Constraints};
use AtolOnline\Exceptions\{
InvalidEntityInCollectionException,
TooLongCashierException};
use Exception;
use JetBrains\PhpStorm\ArrayShape;
@ -28,8 +32,8 @@ class Correction extends Entity
protected Company $company;
/**
* @todo вынести в трейт?
* @var string|null ФИО кассира
* @todo вынести в трейт
*/
protected ?string $cashier = null;
@ -195,11 +199,11 @@ class Correction extends Entity
* @throws InvalidEntityInCollectionException
*/
#[ArrayShape([
'company' => "\AtolOnline\Entities\Company",
'correction_info' => "\AtolOnline\Entities\CorrectionInfo",
'payments' => "array",
'vats' => "\AtolOnline\Collections\Vats|null",
'cashier' => "null|string"
'company' => '\AtolOnline\Entities\Company',
'correction_info' => '\AtolOnline\Entities\CorrectionInfo',
'payments' => 'array',
'vats' => '\AtolOnline\Collections\Vats|null',
'cashier' => 'null|string',
])]
public function jsonSerialize(): array
{

View File

@ -7,7 +7,9 @@
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
declare(strict_types=1);
/** @noinspection PhpMultipleClassDeclarationsInspection */
declare(strict_types = 1);
namespace AtolOnline\Entities;

View File

@ -556,8 +556,8 @@ final class Item extends Entity
if (is_string($declaration_number)) {
$declaration_number = trim($declaration_number);
if (
mb_strlen($declaration_number) < Constraints::MIN_LENGTH_DECLARATION_NUMBER ||
mb_strlen($declaration_number) > Constraints::MAX_LENGTH_DECLARATION_NUMBER
mb_strlen($declaration_number) < Constraints::MIN_LENGTH_DECLARATION_NUMBER
|| mb_strlen($declaration_number) > Constraints::MAX_LENGTH_DECLARATION_NUMBER
) {
throw new InvalidDeclarationNumberException($declaration_number);
}

View File

@ -11,10 +11,19 @@ declare(strict_types = 1);
namespace AtolOnline\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Enums\PaymentTypes;
use AtolOnline\Exceptions\{InvalidEnumValueException, NegativePaymentSumException, TooHighPaymentSumException,};
use JetBrains\PhpStorm\{ArrayShape, Pure};
use AtolOnline\{
Constants\Constraints,
Enums\PaymentTypes,
};
use AtolOnline\Exceptions\{
InvalidEnumValueException,
NegativePaymentSumException,
TooHighPaymentSumException,
};
use JetBrains\PhpStorm\{
ArrayShape,
Pure
};
/**
* Класс, описывающий оплату

View File

@ -34,8 +34,8 @@ final class Receipt extends Entity
protected Client $client;
/**
* @todo вынести в трейт?
* @var Company Продавец
* @todo вынести в трейт
*/
protected Company $company;
@ -55,8 +55,8 @@ final class Receipt extends Entity
protected Items $items;
/**
* @todo вынести в трейт?
* @var Payments Коллекция оплат
* @todo вынести в трейт
*/
protected Payments $payments;
@ -71,8 +71,8 @@ final class Receipt extends Entity
protected float $total = 0;
/**
* @todo вынести в трейт?
* @var string|null ФИО кассира
* @todo вынести в трейт
*/
protected ?string $cashier = null;
@ -204,10 +204,9 @@ final class Receipt extends Entity
*
* @param Items $items
* @return $this
* @throws EmptyItemsException
* @throws InvalidEntityInCollectionException
* @throws Exception
* @todo исключение при пустой коллекции
* @throws EmptyItemsException
*/
public function setItems(Items $items): self
{

View File

@ -106,7 +106,8 @@ final class Vat extends Entity
#[Pure]
public function getCalculated(): float
{
return Helpers::toRub(match ($this->getType()) {
return Helpers::toRub(
match ($this->getType()) {
VatTypes::VAT10 => Helpers::toKop($this->sum) * 10 / 100,
VatTypes::VAT18 => Helpers::toKop($this->sum) * 18 / 100,
VatTypes::VAT20 => Helpers::toKop($this->sum) * 20 / 100,
@ -114,7 +115,8 @@ final class Vat extends Entity
VatTypes::VAT118 => Helpers::toKop($this->sum) * 18 / 118,
VatTypes::VAT120 => Helpers::toKop($this->sum) * 20 / 120,
default => 0,
});
}
);
}
/**

View File

@ -77,6 +77,7 @@ final class PaymentObjects extends Enum
/**
* Составной предмет расчёта
*
* @deprecated Более не используется согласно ФФД 1.05
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25 (payment_object)
*/

View File

@ -12,6 +12,7 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions;
use Exception;
use JetBrains\PhpStorm\Pure;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
@ -29,6 +30,7 @@ class AtolException extends Exception
* @param string $message Сообщение
* @param int[] $ffd_tags Переопредление тегов ФФД
*/
#[Pure]
public function __construct(string $message = '', array $ffd_tags = [])
{
$tags = implode(', ', $ffd_tags ?: $this->ffd_tags);

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
use AtolOnline\Api\KktResponse;
use Exception;
use JetBrains\PhpStorm\Pure;
/**
* Исключение, возникающее при неудачной авторизации
@ -25,6 +26,7 @@ class AuthFailedException extends Exception
* @param KktResponse $response
* @param string $message
*/
#[Pure]
public function __construct(KktResponse $response, string $message = '')
{
parent::__construct(($message ?: 'Ошибка авторизации: ') . ': ' . $response);

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать пустой логин
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12
*/
class EmptyLoginException extends AtolException

View File

@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать пустой пароль
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12
*/
class EmptyPasswordException extends AtolException

View File

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

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline\Exceptions;
use JetBrains\PhpStorm\Pure;
/**
* Исключение, возникающее при ошибке валидации UUID
*/
@ -19,13 +21,11 @@ class InvalidUuidException extends AtolException
/**
* Конструктор
*
* @param $uuid
* @param string $message
* @param int $code
* @param Throwable|null $previous
* @param string $uuid
*/
public function __construct(?string $uuid = null)
#[Pure]
public function __construct(string $uuid = '')
{
parent::__construct('Невалидный UUID' . ($uuid ? ': ' . $uuid : ''));
parent::__construct('Невалидный UUID: ' . $uuid);
}
}

View File

@ -11,6 +11,8 @@ declare(strict_types = 1);
namespace AtolOnline;
use JetBrains\PhpStorm\ArrayShape;
/**
* Константы, определяющие параметры тестовых сред
*
@ -23,6 +25,16 @@ final class TestEnvParams
*
* @return string[]
*/
#[ArrayShape([
'endpoint' => "string",
'company_name' => "string",
'inn' => "string",
'payment_address' => "string",
'group' => "string",
'login' => "string",
'password' => "string",
'endpoint_ofd' => "string",
])]
public static function FFD105(): array
{
return [
@ -43,6 +55,16 @@ final class TestEnvParams
* @return string[]
* @noinspection PhpUnused
*/
#[ArrayShape([
'endpoint' => "string",
'company_name' => "string",
'inn' => "string",
'payment_address' => "string",
'group' => "string",
'login' => "string",
'password' => "string",
'endpoint_ofd' => "string",
])]
public static function FFD12(): array
{
return [