48 Commits

Author SHA1 Message Date
a7205ff754 Переработан Vat, покрыт тестами 2021-11-28 00:58:05 +08:00
e0ff5a261a Новые теги в Ffd105Tags + мелочи 2021-11-28 00:57:19 +08:00
2ebb172f2e Мелочи по конвертации денег 2021-11-28 00:55:34 +08:00
e2141551d5 Enum стал абстрактным внутри Enums, наследникам созданы getFfdTags() 2021-11-28 00:55:28 +08:00
c9670a1321 Удалены бесполезные ItemArray, VatArray и PaymentArray, будут заменены коллекциями 2021-11-28 00:49:53 +08:00
9d2617858d AgentInfo: перенос валидации типа агента из конструктора в сеттер 2021-11-28 00:44:43 +08:00
f548032843 Ссылки на телеграм и патреон 2021-11-27 18:53:23 +08:00
e0d792d3a4 Реализован и покрыт тестами AgentInfo (переименован из Agent) 2021-11-27 17:59:50 +08:00
16c8d8a676 Реализован и покрыт тестами MoneyTransferOperator 2021-11-26 09:16:46 +08:00
8b79b2be51 Мелкофиксы
- `Ffd105Tags::CLIENT_CONTACTS` => `CLIENT_PHONE_EMAIL`
+ мелочи по `Client`, `ClientTest` и `PayingAgentTest`
2021-11-24 18:55:53 +08:00
95dbc3a5b7 Класс Supplier обзавёлся name + inn и допокрыт тестами 2021-11-24 18:54:48 +08:00
790831e933 MoneyTransferOperator => ReceivePaymentsOperator 2021-11-24 18:45:01 +08:00
3b75c8b983 Класс Supplier, покрытый тестами
Это было слишком легко :(
2021-11-24 17:57:24 +08:00
c5b57ec26d Класс MoneyTransferOperator, покрытый тестами
Также мелкофиксы по phpdoc `PayingAgent` и его тестам
2021-11-24 17:54:04 +08:00
42d194116f Туча доработок
- класс `PayingAgent`, покрытый тестами
- новые константы для тегов ФФД 1.05 `Ffd105Tags`
- `Entity::jsonSerialize()` object -> array (again)
- `TooManyException::$max` int -> float
- тесты по psr-4, потому что почему бы и нет
- некоторые провайдеры вынесены в `BasicTestCase`
- улучшен тест покупателя
2021-11-24 01:30:54 +08:00
b5a01debd2 Новый енам AgentTypes 2021-11-22 14:53:10 +08:00
9d3344a0df Мелкофиксы в конструкторе KktMonitor 2021-11-22 14:52:27 +08:00
00b2643f42 Новые функции в Helpers для проверки классов 2021-11-22 14:52:05 +08:00
3bf8667437 Рефакторинг исключений, новые константы ограничений
Описывать все слишком долго, TLDR:
- упрощены корневые AtolException, {BasicTooLongException => TooLongException}, {BasicTooManyException => TooManyException}
- InvalidSnoException заменён на InvalidEnumValueException
- добавлены новые константы, общий порядок изменён в соответствии с порядком упоминания в документации, ссылки на которую тоже добавлены с указанием страниц

Помимо этого, в enum-ах теперь должен быть предусмотрен метод getFfdTags()
2021-11-22 14:51:10 +08:00
e1303f4fdb Правки по документации
- в мониторинг добавлено о приведении к строке
- актуализированы Client и Company
- мелочи в соотв. классах
2021-11-21 19:06:55 +08:00
920c08c610 Правки по документации
- добавлено про мониторинг
- убрано про `base_name` из коррекции
2021-11-21 18:58:03 +08:00
d281071970 Удалён метод AtolOnline\Entities\Kkt::getNetworkError() за ненадобностью 2021-11-21 18:57:20 +08:00
95499174b0 Warning in readme 2021-11-21 00:33:43 +08:00
6d0cac2cbc Первая итерация рефаторинга фискализатора 2021-11-21 00:19:35 +08:00
6620acf1bf Мелкофикс теста KktMonitor::GetOne() 2021-11-21 00:17:40 +08:00
e89369348a Метод KktMonitor::auth() перенесён в AtolClient для совместимости в фискализатором 2021-11-21 00:15:59 +08:00
b35b9bfa87 Удалена поддержка base_name в документе коррекции (#5) 2021-11-20 23:46:39 +08:00
e1120051c1 Новая сущность Kkt для ответов мониторинга (#8) + мелкий рефакторинг
- enum-константы перенесены в своё пространство Enums
- новые исключения EmptyMonitorDataException + NotEnoughMonitorDataException
- KktMonitor::getAll() теперь возвращает коллекцию объектов Kkt
- KktMonitor::getOne() теперь возвращает объект Kkt
- местами актуализированы return types + phpdoc

Покрытие тестами:
- 61% исключений
- 98% AtolClient (пока хз как покрыть 208-ую строку)
- 100% KktMonitor
- 100% Kkt
- 100% Client
- 100% Company
- 100% Entity
2021-11-20 23:39:08 +08:00
6551366d84 Полное покрытие тестами классов AtolClient + KktMonitor + половины исключений
Часть тестов завязаны на тестовый API мониторинга Атола. Иногда он закашливается и не отвечает, возможно, там рейтлимит. Да и пофиг, моки -- злейшее зло, и мне лень их писать.
2021-11-19 18:42:14 +08:00
2c5144caac KktMonitor::getAll() теперь возвращает коллекцию объектов ККТ
При этом остаётся возможность получить полный ответ через KktMonitor::getResponse()
2021-11-19 18:29:09 +08:00
92a2c6cc48 Переработаны существующие и дописаны новые тест AtolClient
Используется тестовый API АТОЛ Онлайн. 94% покрытие AtolClient, но ещё не закончено
2021-11-19 13:42:51 +08:00
949b31a85a [WIP] Начало работы над тестом KktMonitorTest 2021-11-18 19:07:32 +08:00
03591600dd Поддержка мониторинга (#8) и рефакторинг
- абстрактный класс AtolClient:
    - больше не наследуется от клиента guzzle, но содержит его объект
    - из Kkt вынесены методы, отвечающие за формирование запроса, отправку и получение ответа, в т.ч. авторизацию
- переименованы исключения TooLongKktLoginException, TooLongKktPasswordException, EmptyKktLoginException и EmptyKktPasswordException
- мелочи по AuthFailedException
- заготовки тестов AtolClient и KktMonitor
2021-11-18 12:24:44 +08:00
77481884ad Начало работы по #5 и #6
- строгая типизация
- переработан класс `TestEnvParams`:
    - вынесен на уровень выше из под `AtolOnline\Constants`
    - вместо констант - две функции для получения актуальных параметров подключения по ФФД1.05 и ФФД1.2
- актуализированы `PaymentObjects` согласно #5
- исходники вынесены не уровень выше в `src`
- константы теперь enum через `myclabs/php-enum`
- новые константы `DocumentTypes`
- классы констант финализированы
- все исключения переименованы, а многие так или иначе отрефакторены (не полностью)
- новые исключения `InvalidSnoException`, `InvalidPaymentAddressException`
- `helpers.php` стал полноценным классом `Helpers`
- удалены трейты `HasEmail`, `HasInn`, `RublesKopeksConverter` (конвертация перенесена в `Helpers`)
- удалён хелпер `valid_strlen()`, вместо него теперь везде `mb_strlen()`
- сущности `Client` и `Company` получили свои имплементации для `email` и `inn`
- доработки в `BasicTestCase`
- полное покрытие тестами: `Client`, `Company`, `Helpers`
- поправлен `phpunit.xml`
- везде обновлены копирайты
- актуализированы и исправлены phpdoc, return types
- начато введение `strict_types=1`
- минимальный php теперь 8.0
- обновлены все зависимости
- подключен пакет коллекций laravel для будущего использования
- теперь можно `composer test` и `composer test-cov`
2021-11-18 12:24:30 +08:00
cc944a1dbb Бейджи в README 2021-05-25 11:48:37 +08:00
bb05f0c752 Merge branch 'master' into dev 2021-05-25 00:58:53 +08:00
1c76608468 Github Actions разграничены для веток master и dev 2021-05-24 23:40:46 +08:00
bd6e208216 Обновление composer.json и первичных текстовок 2021-05-24 23:00:34 +08:00
7b4411ec01 Обновление зависимостей 2021-05-24 22:45:03 +08:00
23fa1f7eb9 Update FUNDING.yml 2021-05-24 12:38:39 +08:00
929bf84c97 Merge pull request #3 from komantnick/develop
Исправление багов при отправке сырого JSON для чека коррекции с исправлениями
2021-05-24 10:55:09 +08:00
Nikita Saiapin
5c1c4dba12 Фикс размера НДС 2021-05-23 22:35:45 +08:00
Nikita Saiapin
4c40bebe14 Исправление багов при отправке сырого JSON чека коррекции 2021-05-22 21:48:12 +08:00
d321205ac9 Фикс выброса AtolAuthFailedException 2020-10-13 02:06:13 +08:00
387e6e445f Удалён сервер discord и его упоминание в README 2020-06-14 13:52:07 +08:00
3bd043bde7 Kkt::setCallbackUrl() - Фикс проверки callback_url по регулярке 2020-06-14 13:44:35 +08:00
7c8ee85b89 Фикс регулярки callback_url 2020-06-14 13:44:04 +08:00
aab68646e6 Фикс сообщений в исключениях 2020-06-14 13:43:32 +08:00
134 changed files with 8366 additions and 4048 deletions

6
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
custom: ['https://money.yandex.ru/to/41001685237530']
github: anthonyaxenov
patreon: anthonyaxenov
custom: [ 'https://yoomoney.ru/to/41001685237530' ]

25
.github/workflows/dev.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Dev build
on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]
jobs:
Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate composer.json and composer.lock
uses: php-actions/composer@40-env
with:
version: 2
php_version: 8.0
only_args: --prefer-dist --no-progress
- name: Run phpunit tests
uses: php-actions/phpunit@v9
with:
configuration: ./phpunit.xml

25
.github/workflows/master.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Master build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate composer.json and composer.lock
uses: php-actions/composer@40-env
with:
version: 2
php_version: 7.4
only_args: --prefer-dist --no-progress
- name: Run phpunit tests
uses: php-actions/phpunit@v8
with:
configuration: ./phpunit.xml

View File

@@ -1,26 +0,0 @@
name: Build
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install composer dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Run phpunit tests
uses: php-actions/phpunit@v1.0.0
with:
# Configuration file location
config: ./phpunit.xml

View File

@@ -1,6 +1,17 @@
# АТОЛ Онлайн
![Build](https://github.com/anthonyaxenov/atol-online/workflows/Build/badge.svg?branch=master)
---
**В этой ветке проводится глубокий рефакторинг и активная подготовка к `v1.0.0`.**
**Актуальность документации -- околонулевая.**
**Общая работоспособность -- нулевая.**
---
[![Master build](https://github.com/anthonyaxenov/atol-online/actions/workflows/master.yml/badge.svg)](https://github.com/anthonyaxenov/atol-online/actions/workflows/master.yml)
[![Dev build](https://github.com/anthonyaxenov/atol-online/actions/workflows/dev.yml/badge.svg)](https://github.com/anthonyaxenov/atol-online/actions/workflows/dev.yml)
Библиотека для фискализации чеков по 54-ФЗ через [облачную ККТ АТОЛ](https://online.atol.ru/).
@@ -10,7 +21,7 @@
## Системные требования
* PHP 7.2+
* PHP 7.4+
* composer
* php-json
@@ -103,8 +114,6 @@
## Дополнительные ресурсы
**Discord-сервер для обсуждения этой библиотеки: [discord.gg/mFYTQmp](https://discord.gg/mFYTQmp)**
Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md)
Официальные ресурсы АТОЛ Онлайн:

View File

@@ -25,7 +25,8 @@
- [ ] Тесты для регистрации документа расхода
- [ ] Тесты для регистрации документа возврата расхода
- [ ] Тесты для регистрации документа коррекции расхода
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках. Рубли (дроби) должны быть только в JSON-представлениях
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках.
Рубли (дроби) должны быть только в JSON-представлениях
## Поддержка методов API (регистрация документов)
@@ -63,18 +64,3 @@
- [x] Пoддержка `correction.vats` (обязательный)
- [x] Пoддержка `correction.correction_info` (обязательный)
- [x] Пoддержка `correction.cashier`
## Не будут реализовываться
### Валидация генерируемых документов согласно актуальной схемы API
- Валидатор схемы для документов прихода, возврата прихода, расхода, возврата расхода
- Валидатор схемы для документов коррекции прихода, коррекции расхода
1. Отказ обусловлен скоростью выполнения.
Базовая реализация, которая была начата, подразумевала синглтон, который кешировал однажды полученную схему.
Практика показала, что этот единичный запрос может существенно тормозить работу сервера и в течение долгого времени
не отдавать ответ клиенту.
2. Такая валидация подходит в том случае, если бы при разработке использовалась концепция IoC.
До версии пакета 2.0.0 таких серьёзных имзенений не планируется.

View File

@@ -5,16 +5,21 @@
"type": "library",
"keywords": [
"54-fz",
"54-фз",
"kkt",
"ккт",
"e-commerce",
"cash",
"cash register",
"payment",
"payment system",
"atol",
"atol-online"
"атол",
"atol-online",
"атол онлайн"
],
"homepage": "https://github.com/anthonyaxenov/atol-online",
"readme": "https://github.com/anthonyaxenov/atol-online/blob/master/README.md",
"authors": [
{
"name": "Anthony Axenov",
@@ -23,31 +28,47 @@
}
],
"support": {
"rss": "https://t.me/atolonline_php",
"chat": "https://t.me/+Rky7ia68fctjZmUy",
"source": "https://github.com/anthonyaxenov/atol-online",
"issues": "https://github.com/anthonyaxenov/atol-online/issues",
"chat": "https://discord.gg/mFYTQmp"
"docs": "https://github.com/anthonyaxenov/atol-online/blob/master/docs/readme.md"
},
"funding": [
{
"type": "Patreon",
"url": "https://www.patreon.com/anthonyaxenov"
},
{
"type": "Yoomoney",
"url": "https://yoomoney.ru/to/41001685237530"
}
],
"require": {
"php": ">=7.2",
"php": ">=8.0",
"ext-json": "*",
"guzzlehttp/guzzle": "^6.5",
"psr/log": "^1.1",
"ramsey/uuid": "^3.9"
"ext-mbstring": "*",
"guzzlehttp/guzzle": "^7.4",
"psr/log": "^3",
"ramsey/uuid": "^4.2",
"myclabs/php-enum": "^1.8",
"illuminate/collections": "^8.70"
},
"require-dev": {
"phpunit/phpunit": "^8.5"
"phpunit/phpunit": "^9.5"
},
"autoload": {
"psr-4": {
"AtolOnline\\": "src/"
}
},
"autoload-dev": {
"classmap": [
"src/AtolOnline/Api/",
"src/AtolOnline/Exceptions/",
"src/AtolOnline/Entities/",
"src/AtolOnline/Traits/",
"src/AtolOnline/Constants/",
"tests/"
],
"files": [
"src/helpers.php"
]
},
"scripts": {
"test": "vendor/bin/phpunit --colors=always",
"test-cov": "vendor/bin/phpunit --coverage-html coverage"
}
}

2366
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,37 +25,21 @@ $customer = new AtolOnline\Entities\Client();
// 1 способ - через конструктор
$customer = new AtolOnline\Entities\Client(
'John Doe', // наименование
'+1/22/99*73s dsdas654 5s6', // номер телефона +122997365456
'john@example.com', // email
'+1/22/99*73s dsdas654 5s6', // номер телефона +122997365456
'+fasd3\qe3fs_=nac990139928czc' // номер ИНН 3399013928
);
// 2 способ - через сеттеры
$customer = (new AtolOnline\Entities\Client())
->setEmail('john@example.com')
->setInn('+fasd3\q3fs_=nac9901 3928c-c') // 3399013928
->setInn('+fasd3\q3fs_=nac9901 3928c-c')
->setName('John Doe')
->setPhone('+1/22/99*73s dsdas654 5s6'); // +122997365456
->setPhone('+1/22/99*73s dsdas654 5s6');
// либо комбинация этих способов
```
Метод `setEmail()` проверяет входную строку на длину (до 64 символов) и валидность формата email.
Выбрасывает исключения:
* `AtolEmailTooLongException` (если слишком длинный email);
* `AtolEmailValidateException` (если email невалиден).
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
Метод `setName()` проверяет входную строку на длину (до 256 символов).
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное имя).
Метод `setPhone()` чистит входную строку от всех символов, кроме цифр и знака `+`, и проверяет длину (до 64 символов).
Выбрасывает исключение `AtolPhoneTooLongException` (если слишком длинный номер телефона).
Конструктор может выбрасывать любое из указанных выше исключений, если в него передаются значения.
Получить установленные значения атрибутов можно через геттеры:
```php
@@ -65,7 +49,7 @@ $customer->getName();
$customer->getPhone();
```
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
Объект класса приводится к JSON-строке автоматически или принудительно:
```php
echo $customer;
@@ -80,4 +64,4 @@ $json_array = $customer->jsonSerialize();
---
[Вернуться к содержанию](readme.md)
[Вернуться к содержанию](readme.md)

View File

@@ -22,16 +22,16 @@ $customer = new AtolOnline\Entities\Company();
Указать эти атрибуты можно двумя способами:
```php
// 1 способ - через конструктор
// 1 способ - через конструктор (все аргументы обязательны)
$company = new AtolOnline\Entities\Company(
'company@example.com' // email
AtolOnline\Constants\SnoTypes::OSN, // тип СНО
'5544332219', // номер ИНН
'https://v4.online.atol.ru', // адрес места расчётов
'company@example.com' // email
);
// 2 способ - через сеттеры
$company = (new AtolOnline\Entities\Company())
$company
->setEmail('company@example.com')
->setInn('5544332219')
->setSno(AtolOnline\Constants\SnoTypes::USN_INCOME)
@@ -40,19 +40,6 @@ $company = (new AtolOnline\Entities\Company())
// либо комбинация этих способов
```
Метод `setEmail()` проверяет входную строку на длину (до 64 символов) и валидность формата email.
Выбрасывает исключения:
* `AtolEmailTooLongException` (если слишком длинный email);
* `AtolEmailValidateException` (если email невалиден).
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
Метод `setPaymentAddress()` проверяет длину (до 256 символов).
Выбрасывает исключение `AtolPaymentAddressTooLongException` (если слишком длинный адрес места расчётов).
Конструктор может выбрасывать любое из указанных выше исключений, если в него передаются параметры.
Получить установленные значения параметров можно через геттеры:
```php
@@ -62,7 +49,7 @@ $company->getPaymentAddress();
$company->getSno();
```
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
Объект класса приводится к JSON-строке автоматически или принудительно:
```php
echo $company;

View File

@@ -13,8 +13,7 @@ $info = new AtolOnline\Entities\CorrectionInfo();
У объекта должны быть указаны все следующие обязательные атрибуты:
* тип коррекции (тег ФФД 1173) - все типы перечислены в классе `AtolOnline\Constants\CorrectionTypes`;
* дата документа основания для коррекции в формате `d.m.Y` (тег ФФД 1178);
* номер документа основания для коррекции (тег ФФД 1179);
* описание коррекции (тег ФФД 1177).
* номер документа основания для коррекции (тег ФФД 1179).
Указать эти атрибуты можно двумя способами:
@@ -26,14 +25,12 @@ $info = new CorrectionInfo(
CorrectionTypes::SELF, // тип коррекции
'01.01.2019', // дата документа коррекции
'12345', // номер документа коррекции
'test' // описание коррекции
);
// 2 способ - через сеттеры
$info = (new CorrectionInfo())
->setType(CorrectionTypes::INSTRUCTION)
->setDate('01.01.2019')
->setName('test')
->setNumber('9999');
// либо комбинация этих способов
@@ -44,7 +41,6 @@ $info = (new CorrectionInfo())
```php
$info->getType();
$info->getDate();
$info->getName();
$info->getNumber();
```

116
docs/monitoring.md Normal file
View File

@@ -0,0 +1,116 @@
# Мониторинг ККТ
[Вернуться к содержанию](readme.md)
---
Библиотека предоставляет возможность следить за состоянием ваших облачных ККТ через API.
## Инициализация
Для этого следует использовать класс `KktMonitor`:
```php
// можно передать параметры подключения в конструктор
$monitor = new AtolOnline\Api\KktMonitor(
login: 'mylogin',
password: 'qwerty'
);
// можно - отдельными сеттерами
$monitor = new AtolOnline\Api\KktMonitor();
->setLogin($credentials['login'])
->setPassword($credentials['password']);
```
Логин и пароль для мониторинга те же, что для регистрации документов.
**По умолчанию монитор работает в тестовом режиме.**
Перевести его в боевой режим можно:
```php
// передачей в конструктор `false` первым параметром:
$monitor = new AtolOnline\Api\KktMonitor(false, /*...*/);
// или отдельным сеттером
$monitor->setTestMode(false);
```
**Тестовый режим** нужен для проверки работоспособности библиотеки и API АТОЛ.
**В боевом режиме** можно получать данные по своим ККТ.
## Получение данных обо всех своих ККТ
Для получения данных обо всех своих ККТ следует вызвать метод `AtolOnline\Api\KktMonitor::getAll()`:
```php
$kkts = $monitor->getAll();
```
В ответе будет итерируемая коллекция объектов `AtolOnline\Entities\Kkt`. Каждый из этих объектов содержит атрибуты:
```php
// для примера получим первую ККТ из всех
$kkt = $kkts->first();
// посмотрим на её атрибуты:
$kkt->serialNumber; // Заводской номер ККТ
$kkt->registrationNumber; // Регистрационный номер машины (РНМ)
$kkt->deviceNumber; // Номер автоматического устройства (внутренний идентификатор устройства)
$kkt->fiscalizationDate; // Дата активации (фискализации) ФН с указанием таймзоны
$kkt->fiscalStorageExpiration; // Дата замены ФН (Срок действия ФН), с указанием таймзоны
$kkt->signedDocuments; // Количество подписанных документов в ФН
$kkt->fiscalStoragePercentageUse; // Наполненость ФН в %
$kkt->fiscalStorageINN; // ИНН компании (указанный в ФН)
$kkt->fiscalStorageSerialNumber; // Заводской (серийный) номер ФН
$kkt->fiscalStoragePaymentAddress; // Адрес расчёта, указанный в ФН
$kkt->groupCode; // Код группы кассы
$kkt->timestamp; // Время и дата формирования данных, UTC
$kkt->isShiftOpened; // Признак открыта смена (true) или закрыта (false)
$kkt->shiftNumber; // Номер смены (или "Номер закрытой смены", когда смена закрыта)
$kkt->shiftReceipt; // Номер документа за смену (или "Кол-во чеков закрытой смены", когда смена закрыта)
$kkt->unsentDocs; // Количество неотправленных документов. Указывается, если значение отлично от 0.
$kkt->firstUnsetDocTimestamp; // Дата первого неотправленного документа. Указывается, если есть неотправленные документы.
$kkt->networkErrorCode; // Код ошибки сети
```
Эти поля описаны в документации мониторинга на [стр. 11](https://online.atol.ru/files/API_service_information.pdf)
Сопоставления кодов ошибок и их описаний доступны в массиве `AtolOnline\Entities\Kkt::ERROR_CODES`.
Объект класса приводится к JSON-строке автоматически или принудительно:
```php
echo $kkt;
$json_string = (string)$kkt;
```
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
```php
$json_array = $kkt->jsonSerialize();
```
## Получение данных об одной из своих ККТ
Для этого следует вызвать метод `AtolOnline\Api\KktMonitor::getOne()`, передав на вход серийный номер (`serialNumber`)
нужной ККТ:
```php
$kkt = $monitor->getOne($kkts->first()->serialNumber);
```
Метод вернёт единственный объект `AtolOnline\Entities\Kkt` с атрибутами, описанными выше.
## Получение последнего ответа от сервера
Класс `AtolOnline\Api\KktMonitor` расширяет абстрактный класс `AtolOnline\Api\AtolClient`.
Это значит, что последний ответ от API АТОЛ всегда сохраняется объектом класса `AtolOnline\Api\KktResponse`. К нему
можно обратиться через метод `AtolOnline\Api\KktMonitor::getResponse()`, независимо от того, что возвращают другие
методы монитора.
---
[Вернуться к содержанию](readme.md)

View File

@@ -1,4 +1,4 @@
# Документация к библиотеке atol-online
# Документация к библиотеке
Содержание:
1. [Работа с клиентами (покупателями)](client.md)
@@ -9,7 +9,8 @@
6. [Работа с данными коррекции](correction_info.md)
7. [Работа с документами](documents.md)
8. [Работа с ККТ](kkt.md)
9. [Мониторинг ККТ](monitoring.md)
---
Если вы нашли опечатку или какое-то несоответствие — делайте pull-request.
Если вы нашли опечатку или какое-то несоответствие — делайте pull-request.

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
~
~ This code is licensed under MIT.
~ Этот код распространяется по лицензии MIT.
~ https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
-->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
@@ -11,15 +19,14 @@
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Unit">
<file>ClientTest.php</file>
<file>CompanyTest.php</file>
<file>VatTest.php</file>
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<file>ItemTest.php</file>
<directory suffix="Test.php">./tests/Feature</directory>
<testsuite name="All">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
</phpunit>

311
src/Api/AtolClient.php Normal file
View File

@@ -0,0 +1,311 @@
<?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\Api;
use AtolOnline\{
Constants\Constraints,
Exceptions\AuthFailedException,
Exceptions\EmptyLoginException,
Exceptions\EmptyPasswordException,
Exceptions\TooLongLoginException,
Exceptions\TooLongPasswordException};
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
/**
* Класс для подключения АТОЛ Онлайн API
*/
abstract class AtolClient
{
/**
* @var bool Флаг тестового режима
*/
protected bool $test_mode;
/**
* @var Client HTTP-клиент для работы с API
*/
protected Client $http;
/**
* @var string|null Логин доступа к API
*/
private ?string $login = null;
/**
* @var string|null Пароль доступа к API (readonly)
*/
private ?string $password = null;
/**
* @var string|null Токен авторизации
*/
private ?string $token = null;
/**
* @var KktResponse|null Последний ответ сервера АТОЛ
*/
private ?KktResponse $response;
/**
* Конструктор
*
* @param bool $test_mode
* @param string|null $login
* @param string|null $password
* @param array $config
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @see https://guzzle.readthedocs.io/en/latest/request-options.html Допустимые параметры для $config
*/
public function __construct(
bool $test_mode = true,
?string $login = null,
?string $password = null,
array $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);
}
/**
* Возвращает установленный флаг тестового режима
*
* @return bool
*/
public function isTestMode(): bool
{
return $this->test_mode;
}
/**
* Устанавливает флаг тестового режима
*
* @param bool $test_mode
* @return $this
*/
public function setTestMode(bool $test_mode = true): self
{
$this->test_mode = $test_mode;
return $this;
}
/**
* Возвращает текущий токен авторизации
*
* @return string|null
*/
public function getToken(): ?string
{
return $this->token;
}
/**
* Устанавливает токен авторизации
*
* @param string|null $token
* @return $this
*/
public function setToken(?string $token): self
{
$this->token = $token;
return $this;
}
/**
* Возвращает последний ответ сервера
*
* @return KktResponse|null
*/
public function getResponse(): ?KktResponse
{
return $this->response;
}
/**
* Возвращает логин доступа к API
*
* @return string|null
*/
public function getLogin(): ?string
{
return $this->login;
}
/**
* Устанавливает логин доступа к API
*
* @param string $login
* @return $this
* @throws EmptyLoginException
* @throws TooLongLoginException
*/
public function setLogin(string $login): self
{
$login = trim($login);
if (empty($login)) {
throw new EmptyLoginException();
} elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) {
throw new TooLongLoginException($login);
}
$this->login = $login;
return $this;
}
/**
* Возвращает пароль доступа к API
*
* @return string|null
*/
public function getPassword(): ?string
{
return $this->password;
}
/**
* Устанавливает пароль доступа к API
*
* @param string $password
* @return $this
* @throws EmptyPasswordException Пароль ККТ не может быть пустым
* @throws TooLongPasswordException Слишком длинный пароль ККТ
*/
public function setPassword(string $password): self
{
if (empty($password)) {
throw new EmptyPasswordException();
} elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) {
throw new TooLongPasswordException($password);
}
$this->password = $password;
return $this;
}
/**
* Возвращает набор заголовков для HTTP-запроса
*
* @return array
*/
private function getHeaders(): array
{
$headers['Content-type'] = 'application/json; charset=utf-8';
if ($this->getToken()) {
$headers['Token'] = $this->getToken();
}
return $headers;
}
/**
* Возвращает полный URL для запроса
*
* @param string $method
* @return string
*/
protected function getUrlToMethod(string $method): string
{
return $this->getMainEndpoint() . '/' . trim($method);
}
/**
* Отправляет авторизационный запрос на сервер АТОЛ и возвращает авторизационный токен
*
* @return string|null
* @throws AuthFailedException
* @throws EmptyPasswordException
* @throws EmptyLoginException
* @throws GuzzleException
*/
protected function doAuth(): ?string
{
$result = $this->sendRequest('POST', $this->getAuthEndpoint(), [
'login' => $this->getLogin() ?? throw new EmptyLoginException(),
'pass' => $this->getPassword() ?? throw new EmptyPasswordException(),
]);
if (!$result->isValid() || !$result->getContent()->token) {
throw new AuthFailedException($result);
}
return $result->getContent()?->token;
}
/**
* Отправляет запрос и возвращает декодированный ответ
*
* @param string $http_method Метод HTTP
* @param string $url URL
* @param array|null $data Данные для передачи
* @param array|null $options Параметры Guzzle
* @return KktResponse
* @throws GuzzleException
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
*/
protected function sendRequest(
string $http_method,
string $url,
?array $data = null,
?array $options = null
): KktResponse {
$http_method = strtoupper(trim($http_method));
$options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []);
if ($http_method != 'GET') {
$options['json'] = $data;
}
$response = $this->http->request($http_method, $url, $options);
return $this->response = new KktResponse($response);
}
/**
* Выполняет авторизацию на сервере АТОЛ
*
* Авторизация выолнится только если неизвестен токен
*
* @param string|null $login
* @param string|null $password
* @return bool
* @throws AuthFailedException
* @throws TooLongLoginException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws TooLongPasswordException
* @throws GuzzleException
*/
public function auth(?string $login = null, ?string $password = null): bool
{
if (empty($this->getToken())) {
$login && $this->setLogin($login);
$password && $this->setPassword($password);
if ($token = $this->doAuth()) {
$this->setToken($token);
}
}
return !empty($this->getToken());
}
/**
* Возвращает URL для запроса авторизации
*
* @return string
*/
abstract protected function getAuthEndpoint(): string;
/**
* Возвращает URL для запросов
*
* @return string
*/
abstract protected function getMainEndpoint(): string;
}

378
src/Api/KktFiscalizer.php Normal file
View File

@@ -0,0 +1,378 @@
<?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\Api;
use AtolOnline\{
Constants\Constraints,
Entities\Company,
Entities\Document,
Exceptions\AuthFailedException,
Exceptions\EmptyCorrectionInfoException,
Exceptions\EmptyLoginException,
Exceptions\EmptyPasswordException,
Exceptions\InvalidCallbackUrlException,
Exceptions\InvalidDocumentTypeException,
Exceptions\InvalidInnLengthException,
Exceptions\InvalidUuidException,
Exceptions\TooLongCallbackUrlException,
Exceptions\TooLongLoginException,
Exceptions\TooLongPasswordException,
Exceptions\TooLongPaymentAddressException,
Exceptions\TooManyItemsException,
Exceptions\TooManyVatsException,
TestEnvParams
};
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Ramsey\Uuid\Uuid;
/**
* Класс для регистрации документов на ККТ
*/
class KktFiscalizer extends AtolClient
{
/**
* @var string|null Группа ККТ
*/
private ?string $group = null;
/**
* @var string|null URL для приёма POST-запроса от API АТОЛ с результатом регистрации документа
*/
private ?string $callback_url = null;
/**
* Конструктор
*
* @param bool $test_mode
* @param string|null $login
* @param string|null $password
* @param string|null $group
* @param array $config
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws TooLongLoginException
* @throws TooLongPasswordException
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
*/
public function __construct(
bool $test_mode = true,
?string $login = null,
?string $password = null,
?string $group = null,
array $config = []
) {
parent::__construct($test_mode, $login, $password, $config);
!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
*/
public function getGroup(): ?string
{
return $this->group;
}
/**
* Устанавливает URL для приёма колбеков
*
* @param string $url
* @return $this
* @throws TooLongCallbackUrlException
* @throws InvalidCallbackUrlException
*/
public function setCallbackUrl(string $url): self
{
if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) {
throw new TooLongCallbackUrlException($url, Constraints::MAX_LENGTH_CALLBACK_URL);
} elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) {
throw new InvalidCallbackUrlException('Callback URL not matches with pattern');
}
$this->callback_url = $url;
return $this;
}
/**
* Возвращает URL для приёма колбеков
*
* @return string
*/
public function getCallbackUrl(): string
{
return $this->callback_url;
}
/**
* Регистрирует документ прихода
*
* @param Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyCorrectionInfoException
* @throws InvalidInnLengthException
* @throws TooLongPaymentAddressException
* @throws InvalidDocumentTypeException
* @throws GuzzleException
*/
public function sell(Document $document, ?string $external_id = null): KktResponse
{
if ($document->getCorrectionInfo()) {
throw new EmptyCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('sell', 'receipt', $document, $external_id);
}
/**
* Регистрирует документ возврата прихода
*
* @param Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyCorrectionInfoException
* @throws InvalidInnLengthException
* @throws TooLongPaymentAddressException
* @throws TooManyVatsException
* @throws InvalidDocumentTypeException
* @throws GuzzleException
*/
public function sellRefund(Document $document, ?string $external_id = null): KktResponse
{
if ($document->getCorrectionInfo()) {
throw new EmptyCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id);
}
/**
* Регистрирует документ коррекции прихода
*
* @param Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyCorrectionInfoException
* @throws InvalidInnLengthException
* @throws TooLongPaymentAddressException
* @throws TooManyItemsException
* @throws InvalidDocumentTypeException
* @throws GuzzleException
*/
public function sellCorrection(Document $document, ?string $external_id = null): KktResponse
{
if (!$document->getCorrectionInfo()) {
throw new EmptyCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('sell_correction', 'correction', $document, $external_id);
}
/**
* Регистрирует документ расхода
*
* @param Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyCorrectionInfoException
* @throws InvalidInnLengthException
* @throws TooLongPaymentAddressException
* @throws InvalidDocumentTypeException
* @throws GuzzleException
*/
public function buy(Document $document, ?string $external_id = null): KktResponse
{
if ($document->getCorrectionInfo()) {
throw new EmptyCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy', 'receipt', $document, $external_id);
}
/**
* Регистрирует документ возврата расхода
*
* @param Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyCorrectionInfoException
* @throws InvalidInnLengthException
* @throws TooLongPaymentAddressException
* @throws TooManyVatsException
* @throws InvalidDocumentTypeException
* @throws GuzzleException
*/
public function buyRefund(Document $document, ?string $external_id = null): KktResponse
{
if ($document->getCorrectionInfo()) {
throw new EmptyCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id);
}
/**
* Регистрирует документ коррекции расхода
*
* @param Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException Ошибка авторизации
* @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции
* @throws InvalidInnLengthException Некорректная длтина ИНН
* @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов
* @throws TooManyItemsException Слишком много предметов расчёта
* @throws InvalidDocumentTypeException Некорректный тип документа
* @throws GuzzleException
*/
public function buyCorrection(Document $document, ?string $external_id = null): KktResponse
{
if (!$document->getCorrectionInfo()) {
throw new EmptyCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('buy_correction', 'correction', $document, $external_id);
}
/**
* Проверяет статус чека на ККТ один раз
*
* @param string $uuid UUID регистрации
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidUuidException
* @throws TooLongLoginException
* @throws TooLongPasswordException
*/
public function getDocumentStatus(string $uuid): KktResponse
{
$uuid = trim($uuid);
if (!Uuid::isValid($uuid)) {
throw new InvalidUuidException($uuid);
}
$this->auth();
return $this->sendRequest('GET', 'report/' . $uuid);
}
/**
* Проверяет статус чека на ККТ нужное количество раз с указанным интервалом.
* Вернёт результат как только при очередной проверке сменится статус регистрации документа.
*
* @param string $uuid UUID регистрации
* @param int $retry_count Количество попыток
* @param int $timeout Таймаут в секундах между попытками
* @return KktResponse
* @throws AuthFailedException
* @throws EmptyLoginException
* @throws EmptyPasswordException
* @throws GuzzleException
* @throws InvalidUuidException
* @throws TooLongLoginException
* @throws TooLongPasswordException
*/
public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): KktResponse
{
$try = 0;
do {
$response = $this->getDocumentStatus($uuid);
if ($response->isValid() && $response->getContent()->status == 'done') {
break;
} else {
sleep($timeout);
}
++$try;
} while ($try < $retry_count);
return $response;
}
/**
* Отправляет документ на регистрацию
*
* @param string $api_method Метод API
* @param string $type Тип документа: receipt, correction
* @param Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return KktResponse
* @throws AuthFailedException Ошибка авторизации
* @throws InvalidDocumentTypeException Некорректный тип документа
* @throws InvalidInnLengthException Некорректная длина ИНН
* @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов
* @throws GuzzleException
* @throws Exception
*/
protected function registerDocument(
string $api_method,
string $type,
Document $document,
?string $external_id = null
): KktResponse {
$type = trim($type);
if (!in_array($type, ['receipt', 'correction'])) {
throw new InvalidDocumentTypeException($type);
}
$this->auth();
if ($this->isTestMode()) {
$document->setCompany(new Company(
'test@example.com',
TestEnvParams::FFD105()['sno'],
TestEnvParams::FFD105()['inn'],
TestEnvParams::FFD105()['payment_address'],
));
}
$data['timestamp'] = date('d.m.y H:i:s');
$data['external_id'] = $external_id ?: Uuid::uuid4()->toString();
$data[$type] = $document;
if ($this->getCallbackUrl()) {
$data['service'] = ['callback_url' => $this->getCallbackUrl()];
}
return $this->sendRequest('POST', trim($api_method), $data);
}
/**
* @inheritDoc
*/
protected function getAuthEndpoint(): string
{
return $this->isTestMode()
? 'https://testonline.atol.ru/possystem/v1/getToken'
: 'https://online.atol.ru/possystem/v1/getToken';
}
/**
* @inheritDoc
*/
protected function getMainEndpoint(): string
{
return $this->isTestMode()
? 'https://testonline.atol.ru/possystem/v4/'
: 'https://online.atol.ru/possystem/v4/';
}
}

115
src/Api/KktMonitor.php Normal file
View File

@@ -0,0 +1,115 @@
<?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\Api;
use AtolOnline\Entities\Kkt;
use AtolOnline\Exceptions\EmptyMonitorDataException;
use AtolOnline\Exceptions\NotEnoughMonitorDataException;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection;
/**
* Класс для мониторинга ККТ
*
* @see https://online.atol.ru/files/API_service_information.pdf Документация
*/
class KktMonitor extends AtolClient
{
/**
* @inheritDoc
*/
protected function getAuthEndpoint(): string
{
return $this->isTestMode()
? 'https://testonline.atol.ru/api/auth/v1/gettoken'
: 'https://online.atol.ru/api/auth/v1/gettoken';
}
/**
* @inheritDoc
*/
protected function getMainEndpoint(): string
{
return $this->isTestMode()
? 'https://testonline.atol.ru/api/kkt/v1'
: 'https://online.atol.ru/api/kkt/v1';
}
/**
* Получает от API информацию обо всех ККТ и ФН в рамках группы
*
* @param int|null $limit
* @param int|null $offset
* @return KktResponse
* @throws GuzzleException
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9
*/
protected function fetchAll(?int $limit = null, ?int $offset = null): KktResponse
{
$params = [];
!is_null($limit) && $params['limit'] = $limit;
!is_null($offset) && $params['offset'] = $offset;
return $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params);
}
/**
* Возвращает информацию обо всех ККТ и ФН в рамках группы
*
* @param int|null $limit
* @param int|null $offset
* @return Collection
* @throws GuzzleException
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9
*/
public function getAll(?int $limit = null, ?int $offset = null): Collection
{
$collection = collect($this->fetchAll($limit, $offset)->getContent());
return $collection->map(fn($data) => new Kkt($data));
}
/**
* Получает от API информацию о конкретной ККТ по её серийному номеру
*
* @param string $serial_number
* @return KktResponse
* @throws GuzzleException
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11
*/
protected function fetchOne(string $serial_number): KktResponse
{
return $this->sendRequest(
'GET',
self::getUrlToMethod('cash-registers') . '/' . trim($serial_number),
options: [
'headers' => [
'Accept' => 'application/hal+json',
],
]
);
}
/**
* Возвращает информацию о конкретной ККТ по её серийному номеру
*
* @todo кастовать к отдельному классу со своими геттерами
* @param string $serial_number
* @return Kkt
* @throws GuzzleException
* @throws EmptyMonitorDataException
* @throws NotEnoughMonitorDataException
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11
*/
public function getOne(string $serial_number): Kkt
{
return new Kkt($this->fetchOne($serial_number)->getContent()->data);
}
}

View File

@@ -1,50 +1,54 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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\Api;
use JsonSerializable;
use Psr\Http\Message\ResponseInterface;
use stdClass;
use Stringable;
/**
* Класс AtolResponse, описывающий ответ от ККТ
*
* @property mixed $error
* @package AtolOnline\Api
*/
class KktResponse implements JsonSerializable
class KktResponse implements JsonSerializable, Stringable
{
/**
* @var int Код ответа сервера
*/
protected $code;
protected int $code;
/**
* @var \stdClass Содержимое ответа сервера
* @var stdClass|array|null Содержимое ответа сервера
*/
protected $content;
protected stdClass|array|null $content;
/**
* @var array Заголовки ответа
*/
protected $headers;
protected array $headers;
/**
* AtolResponse constructor.
*
* @param \Psr\Http\Message\ResponseInterface $response
* @param ResponseInterface $response
*/
public function __construct(ResponseInterface $response)
{
$this->code = $response->getStatusCode();
$this->headers = $response->getHeaders();
$this->content = json_decode($response->getBody());
$this->content = json_decode((string)$response->getBody());
}
/**
@@ -63,9 +67,9 @@ class KktResponse implements JsonSerializable
* @param $name
* @return mixed
*/
public function __get($name)
public function __get($name): mixed
{
return $this->getContent()->$name;
return $this->getContent()?->$name;
}
/**
@@ -81,9 +85,9 @@ class KktResponse implements JsonSerializable
/**
* Возвращает объект результата запроса
*
* @return stdClass|null
* @return mixed
*/
public function getContent(): ?stdClass
public function getContent(): mixed
{
return $this->content;
}
@@ -93,18 +97,18 @@ class KktResponse implements JsonSerializable
*
* @return bool
*/
public function isValid()
public function isValid(): bool
{
return !empty($this->getCode())
&& !empty($this->getContent())
&& empty($this->getContent()->error)
&& (int)$this->getCode() < 400;
&& $this->getCode() < 400;
}
/**
* Возвращает текстовое представление
*/
public function __toString()
public function __toString(): string
{
return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE);
}
@@ -112,7 +116,7 @@ class KktResponse implements JsonSerializable
/**
* @inheritDoc
*/
public function jsonSerialize()
public function jsonSerialize(): array
{
return [
'code' => $this->code,

View File

@@ -1,576 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Api;
use AtolOnline\{Constants\Constraints,
Constants\TestEnvParams,
Entities\Company,
Entities\Document,
Exceptions\AtolAuthFailedException,
Exceptions\AtolCallbackUrlTooLongException,
Exceptions\AtolCorrectionInfoException,
Exceptions\AtolInvalidCallbackUrlException,
Exceptions\AtolInvalidUuidException,
Exceptions\AtolKktLoginEmptyException,
Exceptions\AtolKktLoginTooLongException,
Exceptions\AtolKktPasswordEmptyException,
Exceptions\AtolKktPasswordTooLongException,
Exceptions\AtolWrongDocumentTypeException
};
use GuzzleHttp\Client;
use Ramsey\Uuid\Uuid;
/**
* Класс для отправки запросов на ККТ
*
* @package AtolOnline\Api
*/
class Kkt extends Client
{
/**
* @var bool Флаг тестового режима работы
*/
protected $is_test_mode = false;
/**
* @var array Настройки доступа к ККТ
*/
protected $kkt_config = [];
/**
* @var \AtolOnline\Api\KktResponse|null Последний ответ сервера АТОЛ
*/
protected $last_response;
/**
* @var string|null Токен авторизации
*/
private $auth_token;
/**
* Kkt constructor.
*
* @param string|null $group
* @param string|null $login
* @param string|null $pass
* @param bool $test_mode Флаг тестового режима
* @param array $guzzle_config Конфигурация GuzzleHttp
* @throws \AtolOnline\Exceptions\AtolKktLoginEmptyException Логин ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktLoginTooLongException Слишком длинный логин ККТ
* @throws \AtolOnline\Exceptions\AtolKktPasswordEmptyException Пароль ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktPasswordTooLongException Слишком длинный пароль ККТ
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
*/
public function __construct(
?string $group = null,
?string $login = null,
?string $pass = null,
bool $test_mode = false,
array $guzzle_config = []
) {
$this->resetKktConfig();
if ($group) {
$this->setGroup($group);
}
if ($login) {
$this->setLogin($login);
}
if ($login) {
$this->setPassword($pass);
}
$this->setTestMode($test_mode);
$guzzle_config['base_uri'] = $this->getEndpoint();
$guzzle_config['http_errors'] = $guzzle_config['http_errors'] ?? false;
parent::__construct($guzzle_config);
}
/**
* Устанавливает группу доступа к ККТ
*
* @param string $group
* @return $this
*/
public function setGroup(string $group)
{
$this->kkt_config['prod']['group'] = $group;
return $this;
}
/**
* Возвращает группу доступа к ККТ в соответствии с флагом тестового режима
*
* @return string
*/
public function getGroup(): string
{
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['group'];
}
/**
* Устанавливает логин доступа к ККТ
*
* @param string $login
* @return $this
* @throws \AtolOnline\Exceptions\AtolKktLoginEmptyException Логин ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktLoginTooLongException Слишком длинный логин ККТ
*/
public function setLogin(string $login)
{
if (empty($login)) {
throw new AtolKktLoginEmptyException();
} elseif (valid_strlen($login) > Constraints::MAX_LENGTH_LOGIN) {
throw new AtolKktLoginTooLongException($login, Constraints::MAX_LENGTH_LOGIN);
}
$this->kkt_config['prod']['login'] = $login;
return $this;
}
/**
* Возвращает логин доступа к ККТ в соответствии с флагом тестового режима
*
* @return string
*/
public function getLogin(): string
{
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['login'];
}
/**
* Устанавливает пароль доступа к ККТ
*
* @param string $password
* @return $this
* @throws \AtolOnline\Exceptions\AtolKktPasswordEmptyException Пароль ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktPasswordTooLongException Слишком длинный пароль ККТ
*/
public function setPassword(string $password)
{
if (empty($password)) {
throw new AtolKktPasswordEmptyException();
} elseif (valid_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) {
throw new AtolKktPasswordTooLongException($password, Constraints::MAX_LENGTH_PASSWORD);
}
$this->kkt_config['prod']['pass'] = $password;
return $this;
}
/**
* Возвращает логин ККТ в соответствии с флагом тестового режима
*
* @return string
*/
public function getPassword(): string
{
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['pass'];
}
/**
* Устанавливает URL для приёма колбеков
*
* @param string $url
* @return $this
* @throws \AtolOnline\Exceptions\AtolCallbackUrlTooLongException Слишком длинный Callback URL
* @throws \AtolOnline\Exceptions\AtolInvalidCallbackUrlException Невалидный Callback URL
*/
public function setCallbackUrl(string $url)
{
if (valid_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) {
throw new AtolCallbackUrlTooLongException($url, Constraints::MAX_LENGTH_CALLBACK_URL);
} elseif (preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) {
throw new AtolInvalidCallbackUrlException();
}
$this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['callback_url'] = $url;
return $this;
}
/**
* Возвращает URL для приёма колбеков
*
* @return string
*/
public function getCallbackUrl(): string
{
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['callback_url'];
}
/**
* Возвращает последний ответ сервера
*
* @return mixed
*/
public function getLastResponse()
{
return $this->last_response;
}
/**
* Возвращает флаг тестового режима
*
* @return bool
*/
public function isTestMode(): bool
{
return $this->is_test_mode;
}
/**
* Устанавливает флаг тестового режима
*
* @param bool $test_mode
* @return $this
*/
public function setTestMode(bool $test_mode = true)
{
$this->is_test_mode = $test_mode;
return $this;
}
/**
* Регистрирует документ прихода
*
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sell(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('sell', 'receipt', $document, $external_id);
}
/**
* Регистрирует документ возврата прихода
*
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sellRefund(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id);
}
/**
* Регистрирует документ коррекции прихода
*
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sellCorrection(Document $document, ?string $external_id = null)
{
if (!$document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('sell_correction', 'correction', $document, $external_id);
}
/**
* Регистрирует документ расхода
*
* @param \AtolOnline\Entities\Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function buy(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy', 'receipt', $document, $external_id);
}
/**
* Регистрирует документ возврата расхода
*
* @param \AtolOnline\Entities\Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function buyRefund(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id);
}
/**
* Регистрирует документ коррекции расхода
*
* @param \AtolOnline\Entities\Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длтина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function buyCorrection(Document $document, ?string $external_id = null)
{
if (!$document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('buy_correction', 'correction', $document, $external_id);
}
/**
* Проверяет статус чека на ККТ один раз
*
* @param string $uuid UUID регистрации
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolInvalidUuidException Некорректный UUID документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getDocumentStatus(string $uuid)
{
$uuid = trim($uuid);
if (!Uuid::isValid($uuid)) {
throw new AtolInvalidUuidException($uuid);
}
$this->auth();
return $this->sendAtolRequest('GET', 'report/'.$uuid);
}
/**
* Проверяет статус чека на ККТ нужное количество раз с указанным интервалом.
* Вернёт результат как только при очередной проверке сменится статус регистрации документа.
*
* @param string $uuid UUID регистрации
* @param int $retry_count Количество попыток
* @param int $timeout Таймаут в секундах между попытками
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolInvalidUuidException Некорректный UUID документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1)
{
$try = 0;
do {
$response = $this->getDocumentStatus($uuid);
if ($response->isValid() && $response->getContent()->status == 'done') {
break;
} else {
sleep($timeout);
}
++$try;
} while ($try < $retry_count);
return $response;
}
/**
* Возвращает текущий токен авторизации
*
* @return string
*/
public function getAuthToken(): ?string
{
return $this->auth_token;
}
/**
* Устанавливает заранее известный токен авторизации
*
* @param string|null $auth_token
* @return $this
*/
public function setAuthToken(?string $auth_token)
{
$this->auth_token = $auth_token;
return $this;
}
/**
* Сбрасывает настройки ККТ по умолчанию
*/
protected function resetKktConfig(): void
{
$this->kkt_config['prod']['group'] = '';
$this->kkt_config['prod']['login'] = '';
$this->kkt_config['prod']['pass'] = '';
$this->kkt_config['prod']['url'] = 'https://online.atol.ru/possystem/v4';
$this->kkt_config['prod']['callback_url'] = '';
$this->kkt_config['test']['group'] = TestEnvParams::GROUP;
$this->kkt_config['test']['login'] = TestEnvParams::LOGIN;
$this->kkt_config['test']['pass'] = TestEnvParams::PASSWORD;
$this->kkt_config['test']['url'] = 'https://testonline.atol.ru/possystem/v4';
$this->kkt_config['test']['callback_url'] = '';
}
/**
* Возвращает набор заголовков для HTTP-запроса
*
* @return array
*/
protected function getHeaders()
{
$headers['Content-type'] = 'application/json; charset=utf-8';
if ($this->getAuthToken()) {
$headers['Token'] = $this->getAuthToken();
}
return $headers;
}
/**
* Возвращает адрес сервера в соответствии с флагом тестового режима
*
* @return string
*/
protected function getEndpoint(): string
{
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['url'];
}
/**
* Возвращает полный URL до метода API
*
* @param string $to_method
* @param array|null $get_parameters
* @return string
*/
protected function makeUrl(string $to_method, array $get_parameters = null)
{
$url = $this->getEndpoint().($this->getAuthToken() ? '/'.$this->getGroup() : '').'/'.$to_method;
if ($get_parameters && is_array($get_parameters)) {
$url .= '?'.http_build_query($get_parameters);
}
return $url;
}
/**
* Делает запрос, возвращает декодированный ответ
*
* @param string $http_method Метод HTTP (GET, POST и пр)
* @param string $api_method Метод API
* @param mixed $data Данные для передачи
* @param array|null $options Параметры Guzzle
* @return \AtolOnline\Api\KktResponse
* @throws \GuzzleHttp\Exception\GuzzleException
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
*/
protected function sendAtolRequest(string $http_method, string $api_method, $data = null, array $options = null)
{
$http_method = strtoupper($http_method);
$options['headers'] = $this->getHeaders();
$url = $http_method == 'GET'
? $this->makeUrl($api_method, $data)
: $this->makeUrl($api_method, ['token' => $this->getAuthToken()]);
if ($http_method != 'GET') {
$options['json'] = $data;
}
$response = $this->request($http_method, $url, $options);
return $this->last_response = new KktResponse($response);
}
/**
* Производит авторизацию на ККТ и получает токен доступа для дальнейших HTTP-запросов
*
* @return bool
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function auth()
{
if (!$this->getAuthToken()) {
$result = $this->sendAtolRequest('GET', 'getToken', [
'login' => $this->getLogin(),
'pass' => $this->getPassword(),
]);
if (!$result->isValid() || !$result->getContent()->token) {
throw new AtolAuthFailedException($result);
}
$this->auth_token = $result->getContent()->token;
}
return true;
}
/**
* Отправляет документ на регистрацию
*
* @param string $api_method Метод API
* @param string $type Тип документа: receipt, correction
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function registerDocument(string $api_method, string $type, Document $document, ?string $external_id = null)
{
$type = trim($type);
if (!in_array($type, ['receipt', 'correction'])) {
throw new AtolWrongDocumentTypeException($type);
}
$this->auth();
if ($this->isTestMode()) {
$document->setCompany(($document->getCompany() ?: new Company())
->setInn(TestEnvParams::INN)
->setSno(TestEnvParams::SNO)
->setPaymentAddress(TestEnvParams::PAYMENT_ADDRESS));
}
$data['timestamp'] = date('d.m.y H:i:s');
$data['external_id'] = $external_id ?: Uuid::uuid4()->toString();
$data[$type] = $document;
if ($this->getCallbackUrl()) {
$data['service'] = ['callback_url' => $this->getCallbackUrl()];
}
return $this->sendAtolRequest('POST', trim($api_method), $data);
}
}

View File

@@ -1,76 +0,0 @@
<?php
namespace AtolOnline\Constants;
/**
* Класс с константами ограничений: максимальные длины, правила валидации значений
*
* @package AtolOnline\Constants
*/
class Constraints
{
/**
* Максимальная длина Callback URL
*/
const MAX_LENGTH_CALLBACK_URL = 256;
/**
* Максимальная длина email
*/
const MAX_LENGTH_EMAIL = 64;
/**
* Максимальная длина логина ККТ
*/
const MAX_LENGTH_LOGIN = 100;
/**
* Максимальная длина пароля ККТ
*/
const MAX_LENGTH_PASSWORD = 100;
/**
* Максимальная длина имени покупателя
*/
const MAX_LENGTH_CLIENT_NAME = 256;
/**
* Максимальная длина телефона покупателя
*/
const MAX_LENGTH_CLIENT_PHONE = 64;
/**
* Максимальная длина адреса места расчётов
*/
const MAX_LENGTH_PAYMENT_ADDRESS = 256;
/**
* Максимальная длина имени кассира
*/
const MAX_LENGTH_CASHIER_NAME = 64;
/**
* Максимальная длина наименования предмета расчётов
*/
const MAX_LENGTH_ITEM_NAME = 128;
/**
* Максимальная длина единицы измерения предмета расчётов
*/
const MAX_LENGTH_MEASUREMENT_UNIT = 16;
/**
* Максимальная длина пользовательских данных для предмета расчётов
*/
const MAX_LENGTH_USER_DATA = 64;
/**
* Регулярное выражание для валидации строки ИНН
*/
const PATTERN_INN = "/(^[0-9]{10}$)|(^[0-9]{12}$)/";
/**
* Регулярное выражание для валидации строки Callback URL
*/
const PATTERN_CALLBACK_URL = "^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$";
}

View File

@@ -1,53 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
/**
* Константы, определяющие виды оплат. Тег ФФД - 1031, 1081, 1215, 1216, 1217.
*
* @package AtolOnline\Constants
*/
class PaymentTypes
{
/**
* Расчёт наличными. Тег ФФД - 1031.
*/
const CASH = 0;
/**
* Расчёт безналичными. Тег ФФД - 1081.
*/
const ELECTRON = 1;
/**
* Предварительная оплата (зачет аванса). Тег ФФД - 1215.
*/
const PRE_PAID = 2;
/**
* Предварительная оплата (кредит). Тег ФФД - 1216.
*/
const CREDIT = 3;
/**
* Иная форма оплаты (встречное предоставление). Тег ФФД - 1217.
*/
const OTHER = 4;
/**
* Расширенный типы оплаты
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
*/
const ADD_5 = 5;
const ADD_6 = 6;
const ADD_7 = 7;
const ADD_8 = 8;
const ADD_9 = 9;
}

View File

@@ -1,49 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
/**
* Константы, определяющие параметры тестовой среды
*
* @see https://online.atol.ru/files/ffd/test_sreda.txt
* @package AtolOnline\Constants
*/
class TestEnvParams
{
/**
* Логин
*/
const LOGIN = 'v4-online-atol-ru';
/**
* Пароль
*/
const PASSWORD = 'iGFFuihss';
/**
* Группа
*/
const GROUP = 'v4-online-atol-ru_4179';
/**
* Система налогообложения
*/
const SNO = SnoTypes::OSN;
/**
* ИНН
*/
const INN = '5544332219';
/**
* Адрес места расчётов
*/
const PAYMENT_ADDRESS = 'https://v4.online.atol.ru';
}

View File

@@ -1,149 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Entities;
use AtolOnline\{Constants\Constraints, Exceptions\AtolNameTooLongException, Exceptions\AtolPhoneTooLongException, Traits\HasEmail, Traits\HasInn};
/**
* Класс Client, описывающий сущность покупателя
*
* @package AtolOnline\Entities
*/
class Client extends Entity
{
use
/**
* Покупатель может иметь почту. Тег ФФД - 1008.
*/
HasEmail,
/**
* Покупатель может иметь ИНН. Тег ФФД - 1228.
*/
HasInn;
/**
* @var string Телефон покупателя. Тег ФФД - 1008.
*/
protected $phone;
/**
* @var string Имя покупателя. Тег ФФД - 1227.
*/
protected $name;
/**
* Client constructor.
*
* @param string|null $name Наименование
* @param string|null $phone Телефон
* @param string|null $email Email
* @param string|null $inn ИНН
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException Слишком длинный email
* @throws \AtolOnline\Exceptions\AtolEmailValidateException Невалидный email
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolNameTooLongException Слишком длинное имя
* @throws \AtolOnline\Exceptions\AtolPhoneTooLongException СЛишком длинный номер телефона
*/
public function __construct(?string $name = null, ?string $phone = null, ?string $email = null, ?string $inn = null)
{
if ($name) {
$this->setName($name);
}
if ($email) {
$this->setEmail($email);
}
if ($phone) {
$this->setPhone($phone);
}
if ($inn) {
$this->setInn($inn);
}
}
/**
* Возвращает имя покупателя. Тег ФФД - 1227.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Устанавливает имя покупателя
* Тег ФФД - 1227.
*
* @param string $name
* @return $this
* @throws AtolNameTooLongException
*/
public function setName(string $name)
{
$name = trim($name);
if (valid_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) {
throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_CLIENT_NAME);
}
$this->name = $name;
return $this;
}
/**
* Возвращает телефон покупателя.
* Тег ФФД - 1008.
*
* @return string
*/
public function getPhone()
{
return $this->phone ?? '';
}
/**
* Устанавливает телефон покупателя.
* Тег ФФД - 1008.
* Входная строка лишается всех знаков, кроме цифр и знака '+'.
*
* @param string $phone
* @return $this
* @throws AtolPhoneTooLongException
*/
public function setPhone(string $phone)
{
$phone = preg_replace("/[^0-9+]/", '', $phone);
if (valid_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_PHONE) {
throw new AtolPhoneTooLongException($phone, Constraints::MAX_LENGTH_CLIENT_PHONE);
}
$this->phone = $phone;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
$json = [];
if ($this->getName()) {
$json['name'] = $this->getName() ?? '';
}
if ($this->getEmail()) {
$json['email'] = $this->getEmail() ?? '';
}
if ($this->getPhone()) {
$json['phone'] = $this->getPhone() ?? '';
}
if ($this->getInn()) {
$json['inn'] = $this->getInn() ?? '';
}
return $json;
}
}

View File

@@ -1,138 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Entities;
use AtolOnline\{Constants\Constraints,
Exceptions\AtolEmailTooLongException,
Exceptions\AtolEmailValidateException,
Exceptions\AtolInnWrongLengthException,
Exceptions\AtolPaymentAddressTooLongException,
Traits\HasEmail,
Traits\HasInn
};
/**
* Класс, описывающий сущность компании-продавца
*
* @package AtolOnline\Entities
*/
class Company extends Entity
{
use
/**
* Продавец должен иметь почту. Тег ФФД - 1117.
*/
HasEmail,
/**
* Продавец должен иметь ИНН. Тег ФФД - 1018.
*/
HasInn;
/**
* @var string Система налогообложения продавца. Тег ФФД - 1055.
*/
protected $sno;
/**
* @var string Место расчётов (адрес интернет-магазина). Тег ФФД - 1187.
*/
protected $payment_address;
/**
* Company constructor.
*
* @param string|null $sno
* @param string|null $inn
* @param string|null $paymentAddress
* @param string|null $email
* @throws AtolEmailTooLongException Слишком длинный email
* @throws AtolEmailValidateException Невалидный email
* @throws AtolInnWrongLengthException Некорректная длина ИНН
* @throws AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
*/
public function __construct(string $sno = null, string $inn = null, string $paymentAddress = null, string $email = null)
{
if ($sno) {
$this->setSno($sno);
}
if ($inn) {
$this->setInn($inn);
}
if ($paymentAddress) {
$this->setPaymentAddress($paymentAddress);
}
if ($email) {
$this->setEmail($email);
}
}
/**
* Возвращает установленный тип налогообложения. Тег ФФД - 1055.
*
* @return string
*/
public function getSno()
{
return $this->sno;
}
/**
* Устанавливает тип налогообложения. Тег ФФД - 1055.
*
* @param string $sno
* @return $this
*/
public function setSno(string $sno)
{
$this->sno = trim($sno);
return $this;
}
/**
* Возвращает установленный адрес места расчётов. Тег ФФД - 1187.
*
* @return string
*/
public function getPaymentAddress()
{
return $this->payment_address;
}
/**
* Устанавливает адрес места расчётов. Тег ФФД - 1187.
*
* @param string $payment_address
* @return $this
* @throws AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
*/
public function setPaymentAddress(string $payment_address)
{
$payment_address = trim($payment_address);
if (valid_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) {
throw new AtolPaymentAddressTooLongException($payment_address, Constraints::MAX_LENGTH_PAYMENT_ADDRESS);
}
$this->payment_address = $payment_address;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
return [
'email' => $this->getEmail(),
'sno' => $this->getSno(),
'inn' => $this->getInn(),
'payment_address' => $this->getPaymentAddress(),
];
}
}

View File

@@ -1,113 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Entities;
use AtolOnline\Exceptions\AtolTooManyItemsException;
/**
* Класс, описывающий массив предметов расчёта
*
* @package AtolOnline\Entities
*/
class ItemArray extends Entity
{
/**
* Максимальное количество элементов в массиве
* По документации ограничение по количеству предметов расчёта = от 1 до 100,
* однако в схеме sell не указан receipt.properties.items.maxItems
*/
public const MAX_COUNT = 100;
/**
* @var Item[] Массив предметов расчёта
*/
private $items = [];
/**
* ItemArray constructor.
*
* @param Item[]|null $items Массив предметов расчёта
* @throws AtolTooManyItemsException Слишком много предметов расчёта
*/
public function __construct(?array $items = null)
{
if ($items) {
$this->set($items);
}
}
/**
* Устанавливает массив предметов расчёта
*
* @param Item[] $items Массив предметов расчёта
* @return $this
* @throws AtolTooManyItemsException Слишком много предметов расчёта
*/
public function set(array $items)
{
if ($this->validateCount($items)) {
$this->items = $items;
}
return $this;
}
/**
* Добавляет предмет расчёта в массив
*
* @param Item $item Объект предмета расчёта
* @return $this
* @throws AtolTooManyItemsException Слишком много предметов расчёта
*/
public function add(Item $item)
{
if ($this->validateCount()) {
$this->items[] = $item;
}
return $this;
}
/**
* Возвращает массив предметов расчёта
*
* @return Item[]
*/
public function get()
{
return $this->items;
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
$result = [];
foreach ($this->get() as $item) {
$result[] = $item->jsonSerialize();
}
return $result;
}
/**
* Проверяет количество предметов расчёта
*
* @param Item[]|null $items Если передать массив, то проверит количество его элементов.
* Иначе проверит количество уже присвоенных элементов.
* @return bool true если всё хорошо, иначе выбрасывает исключение
* @throws AtolTooManyItemsException Слишком много предметов расчёта
*/
protected function validateCount(?array $items = null): bool
{
if ((!empty($items) && count($items) >= self::MAX_COUNT) || count($this->items) >= self::MAX_COUNT) {
throw new AtolTooManyItemsException(count($items), self::MAX_COUNT);
}
return true;
}
}

View File

@@ -1,111 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Entities;
use AtolOnline\Exceptions\AtolTooManyPaymentsException;
/**
* Класс, описывающий массив оплат
*
* @package AtolOnline\Entities
*/
class PaymentArray extends Entity
{
/**
* Максимальное количество элементов массива
*/
public const MAX_COUNT = 10;
/**
* @var Payment[] Массив оплат
*/
private $payments = [];
/**
* ItemArray constructor.
*
* @param Payment[]|null $payments Массив оплат
* @throws AtolTooManyPaymentsException Слишком много оплат
*/
public function __construct(?array $payments = null)
{
if ($payments) {
$this->set($payments);
}
}
/**
* Устанавливает массив оплат
*
* @param Payment[] $payments
* @return $this
* @throws AtolTooManyPaymentsException Слишком много оплат
*/
public function set(array $payments)
{
if ($this->validateCount($payments)) {
$this->payments = $payments;
}
return $this;
}
/**
* Добавляет новую оплату к заданным
*
* @param Payment $payment Объект оплаты
* @return $this
* @throws AtolTooManyPaymentsException Слишком много оплат
*/
public function add(Payment $payment)
{
if ($this->validateCount()) {
$this->payments[] = $payment;
}
return $this;
}
/**
* Возвращает массив оплат
*
* @return Payment[]
*/
public function get()
{
return $this->payments;
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
$result = [];
foreach ($this->get() as $payment) {
$result[] = $payment->jsonSerialize();
}
return $result;
}
/**
* Проверяет количество налоговых ставок
*
* @param Payment[]|null $payments Если передать массив, то проверит количество его элементов.
* Иначе проверит количество уже присвоенных элементов.
* @return bool true если всё хорошо, иначе выбрасывает исключение
* @throws AtolTooManyPaymentsException Слишком много оплат
*/
protected function validateCount(?array $payments = null): bool
{
if ((!empty($payments) && count($payments) >= self::MAX_COUNT) || count($this->payments) >= self::MAX_COUNT) {
throw new AtolTooManyPaymentsException(count($payments), self::MAX_COUNT);
}
return true;
}
}

View File

@@ -1,187 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Entities;
use AtolOnline\{Constants\VatTypes, Traits\RublesKopeksConverter};
/**
* Класс, описывающий ставку НДС
*
* @package AtolOnline\Entities
*/
class Vat extends Entity
{
use RublesKopeksConverter;
/**
* @var string Выбранный тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
*/
private $type;
/**
* @var int Сумма в копейках, от которой пересчитывается размер НДС
*/
private $sum_original = 0;
/**
* @var int Сумма НДС в копейках
*/
private $sum_final = 0;
/**
* Vat constructor.
*
* @param string $type Тип ставки НДС
* @param float|null $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС
*/
public function __construct(string $type = VatTypes::NONE, float $rubles = null)
{
$this->type = $type;
if ($rubles) {
$this->setSum($rubles);
}
}
/**
* Устанавливает размер НДС от суммы в копейках
*
* @param string $type Тип ставки НДС
* @param int $kopeks Копейки
* @return float|int
* @see https://nalog-nalog.ru/nds/nalogovaya_baza_nds/kak-schitat-nds-pravilno-vychislyaem-20-ot-summy-primer-algoritm/
* @see https://glavkniga.ru/situations/k500734
* @see https://www.b-kontur.ru/nds-kalkuljator-online
*/
protected static function calculator(string $type, int $kopeks)
{
switch ($type) {
case VatTypes::NONE:
case VatTypes::VAT0:
return 0;
case VatTypes::VAT10:
//return $kopeks * 10 / 100;
case VatTypes::VAT110:
return $kopeks * 10 / 110;
case VatTypes::VAT18:
//return $kopeks * 18 / 100;
case VatTypes::VAT118:
return $kopeks * 18 / 118;
case VatTypes::VAT20:
//return $kopeks * 20 / 100;
case VatTypes::VAT120:
return $kopeks * 20 / 120;
}
return 0;
}
/**
* Возвращает тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Устанавливает тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
*
* @param string $type Тип ставки НДС
* @return $this
*/
public function setType(string $type)
{
$this->type = $type;
$this->setFinal();
return $this;
}
/**
* Возвращает расчитанный итоговый размер ставки НДС в рублях. Тег ФФД - 1200.
*
* @return float
*/
public function getFinalSum()
{
return self::toRub($this->sum_final);
}
/**
* Устанавливает исходную сумму, от которой будет расчитываться итоговый размер НДС.
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
*
* @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС
* @return $this
*/
public function setSum(float $rubles)
{
$this->sum_original = self::toKop($rubles);
$this->setFinal();
return $this;
}
/**
* Возвращает исходную сумму, от которой расчитывается размер налога
*
* @return float
*/
public function getSum(): float
{
return self::toRub($this->sum_original);
}
/**
* Прибавляет указанную сумму к общей исходной сумме.
* Автоматически пересчитывает итоговый размер НДС от новой исходной суммы.
*
* @param float $rubles
* @return $this
*/
public function addSum(float $rubles)
{
$this->sum_original += self::toKop($rubles);
$this->setFinal();
return $this;
}
/**
* Расчитывает и возвращает размер НДС от указанной суммы в рублях.
* Не изменяет итоговый размер НДС.
*
* @param float|null $rubles
* @return float
*/
public function calc(float $rubles): float
{
return self::toRub(self::calculator($this->type, self::toKop($rubles)));
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
return [
'type' => $this->getType(),
'sum' => $this->getFinalSum(),
];
}
/**
* Расчитывает и устанавливает итоговый размер ставки от исходной суммы в копейках
*/
protected function setFinal()
{
$this->sum_final = self::calculator($this->type, $this->sum_original);
return $this;
}
}

View File

@@ -1,115 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Entities;
use AtolOnline\Exceptions\AtolTooManyVatsException;
/**
* Класс, описывающий массив ставок НДС
*
* @package AtolOnline\Entities
*/
class VatArray extends Entity
{
/**
* Максимальное количество элементов массива
*/
public const MAX_COUNT = 6;
/**
* @var Vat[] Массив ставок НДС
*/
private $vats = [];
/**
* VatArray constructor.
*
* @param Vat[]|null $vats Массив ставок НДС
* @throws AtolTooManyVatsException Слишком много ставок НДС
*/
public function __construct(?array $vats = null)
{
if ($vats) {
$this->set($vats);
}
}
/**
* Устанавливает массив ставок НДС
*
* @param Vat[] $vats Массив ставок НДС
* @return $this
* @throws AtolTooManyVatsException Слишком много ставок НДС
*/
public function set(array $vats)
{
if ($this->validateCount($vats)) {
$this->vats = $vats;
}
return $this;
}
/**
* Добавляет новую ставку НДС в массив
*
* @param Vat $vat Объект ставки НДС
* @return $this
* @throws AtolTooManyVatsException Слишком много ставок НДС
*/
public function add(Vat $vat)
{
if ($this->validateCount()) {
if (isset($this->vats[$vat->getType()])) {
$this->vats[$vat->getType()]->addSum($vat->getSum());
} else {
$this->vats[$vat->getType()] = $vat;
}
}
return $this;
}
/**
* Возвращает массив ставок НДС
*
* @return Vat[]
*/
public function get()
{
return $this->vats;
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
$result = [];
foreach ($this->get() as $vat) {
$result[] = $vat->jsonSerialize();
}
return $result;
}
/**
* Проверяет количество налоговых ставок
*
* @param Vat[]|null $vats Если передать массив, то проверит количество его элементов.
* Иначе проверит количество уже присвоенных элементов.
* @return bool true если всё хорошо, иначе выбрасывает исключение
* @throws AtolTooManyVatsException Слишком много ставок НДС
*/
protected function validateCount(?array $vats = null): bool
{
if ((!empty($vats) && count($vats) >= self::MAX_COUNT) || count($this->vats) >= self::MAX_COUNT) {
throw new AtolTooManyVatsException(count($vats), self::MAX_COUNT);
}
return true;
}
}

View File

@@ -1,41 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use AtolOnline\Api\KktResponse;
use Exception;
use Throwable;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
*
* @package AtolOnline\Exceptions
*/
class AtolAuthFailedException extends Exception
{
/**
* AtolAuthFailedException constructor.
*
* @param \AtolOnline\Api\KktResponse $last_response
* @param string $message
* @param int $code
* @param \Throwable|null $previous
*/
public function __construct(KktResponse $last_response, $message = "", $code = 0, Throwable $previous = null)
{
$message = $last_response->isValid()
? $message
: '['.$last_response->error->code.'] '.$last_response->error->text.
'. ERROR_ID: '.$last_response->error->error_ID.
'. TYPE: '.$last_response->error->type;
$code = $last_response->isValid() ? $code : $last_response->error->code;
parent::__construct($message, $code, $previous);
}
}

View File

@@ -1,30 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинное имя кассира
*
* @package AtolOnline\Exceptions
*/
class AtolCashierTooLongException extends AtolTooLongException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1021,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Cashier name is too long';
}

View File

@@ -1,23 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке зарегистрировать документ без данных коррекции
*
* @package AtolOnline\Exceptions
*/
class AtolCorrectionInfoException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Document must have correction info';
}

View File

@@ -1,31 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать пустой email
*
* @package AtolOnline\Exceptions
*/
class AtolEmailEmptyException extends AtolException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1008,
1117,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Email cannot be empty';
}

View File

@@ -1,31 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный email
*
* @package AtolOnline\Exceptions
*/
class AtolEmailTooLongException extends AtolTooLongException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1008,
1117,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Email is too long';
}

View File

@@ -1,41 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при ошибке валидации email
*
* @package AtolOnline\Exceptions
*/
class AtolEmailValidateException extends AtolException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1008,
1117,
];
/**
* AtolEmailValidateException constructor.
*
* @param $email
* @param string $message
* @param int $code
* @param \Throwable|null $previous
*/
public function __construct($email, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: 'Invalid email: '.$email, $code, $previous);
}
}

View File

@@ -1,51 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Exception;
use Throwable;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
*
* @package AtolOnline\Exceptions
*/
class AtolException extends Exception
{
/**
* @var int[] Теги ФФД
*/
protected $ffd_tags = null;
/**
* AtolException constructor.
*
* @param string $message
* @param int $code
* @param \Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
if ($this->getFfdTags()) {
$message .= ' [FFD tags: '.implode(', ', $this->getFfdTags()).']';
}
parent::__construct($message, $code, $previous);
}
/**
* Возвращает теги ФФД, с которыми связано исключение
*
* @return array|null
*/
protected function getFfdTags(): ?array
{
return $this->ffd_tags;
}
}

View File

@@ -1,44 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при попытке указать ИНН некорректной длины
*
* @package AtolOnline\Exceptions
*/
class AtolInnWrongLengthException extends AtolException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1016,
1018,
1226,
1228,
];
/**
* AtolInnWrongLengthException constructor.
*
* @param $inn
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($inn, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: 'INN length must be 10 or 12 digits only, but actual is '.
valid_strlen($inn), $code, $previous);
}
}

View File

@@ -1,32 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при работе с невалидным JSON
*
* @package AtolOnline\Exceptions
*/
class AtolInvalidJsonException extends AtolException
{
/**
* AtolInnWrongLengthException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: 'Invalid JSON: ['.json_last_error().'] '.json_last_error_msg(), $code, $previous);
}
}

View File

@@ -1,33 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при ошибке валидации UUID
*
* @package AtolOnline\Exceptions
*/
class AtolInvalidUuidException extends AtolException
{
/**
* AtolInvalidUuidException constructor.
*
* @param $uuid
* @param string $message
* @param int $code
* @param \Throwable|null $previous
*/
public function __construct($uuid, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: 'Invalid UUID: '.$uuid, $code, $previous);
}
}

View File

@@ -1,23 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать пустой логин ККТ
*
* @package AtolOnline\Exceptions
*/
class AtolKktLoginEmptyException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT login cannot be empty';
}

View File

@@ -1,23 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать пустой пароль ККТ
*
* @package AtolOnline\Exceptions
*/
class AtolKktPasswordEmptyException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT password cannot be empty';
}

View File

@@ -1,34 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинное имя
*
* @package AtolOnline\Exceptions
*/
class AtolNameTooLongException extends AtolTooLongException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1026,
1030,
1085,
1225,
1227,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Name is too long';
}

View File

@@ -1,30 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный платёжный адрес
*
* @package AtolOnline\Exceptions
*/
class AtolPaymentAddressTooLongException extends AtolException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1187,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Payment address is too long';
}

View File

@@ -1,34 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный телефон
*
* @package AtolOnline\Exceptions
*/
class AtolPhoneTooLongException extends AtolTooLongException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1008,
1073,
1074,
1075,
1171,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Phone is too long';
}

View File

@@ -1,30 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком высокую цену (сумму)
*
* @package AtolOnline\Exceptions
*/
class AtolPriceTooHighException extends AtolTooManyException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1079,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Price is too high';
}

View File

@@ -1,40 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинное что-либо
*
* @package AtolOnline\Exceptions
*/
class AtolTooLongException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Parameter is too long';
/**
* AtolTooLongException constructor.
*
* @param $string
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($string, $max, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: $this->message.' (max length - '.$max.', actual length - '.
valid_strlen($string), $code, $previous);
}
}

View File

@@ -1,40 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком большое количество чего-либо
*
* @package AtolOnline\Exceptions
*/
class AtolTooManyException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Quantity is too high';
/**
* AtolTooManyException constructor.
*
* @param $quantity
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($quantity, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: $this->message.' (max - '.$max.', actual - '.$quantity.')';
parent::__construct($message, $code, $previous);
}
}

View File

@@ -1,33 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке добавить слишком много платежей в массив
*
* @package AtolOnline\Exceptions
*/
class AtolTooManyPaymentsException extends AtolTooManyException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1031,
1081,
1215,
1217,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Too many payments';
}

View File

@@ -1,35 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке добавить слишком много ставок НДС в массив
*
* @package AtolOnline\Exceptions
*/
class AtolTooManyVatsException extends AtolTooManyException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1102,
1103,
1104,
1105,
1106,
1107,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Too many vats';
}

View File

@@ -1,30 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный телефон
*
* @package AtolOnline\Exceptions
*/
class AtolUnitTooLongException extends AtolTooLongException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1197,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Measurement unit is too long';
}

View File

@@ -1,30 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный дополнительный реквизит
*
* @package AtolOnline\Exceptions
*/
class AtolUserdataTooLongException extends AtolTooLongException
{
/**
* @inheritDoc
*/
protected $ffd_tags = [
1191,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'User data is too long';
}

View File

@@ -1,33 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use Throwable;
/**
* Исключение, возникающее при попытке указать некорректный тип документа
*
* @package AtolOnline\Exceptions
*/
class AtolWrongDocumentTypeException extends AtolException
{
/**
* AtolWrongDocumentTypeException constructor.
*
* @param $type
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($type, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: "Wrong document type: 'receipt' or 'correction' expected, but '$type' provided", $code, $previous);
}
}

View File

@@ -1,55 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Traits;
use AtolOnline\{Constants\Constraints, Exceptions\AtolEmailTooLongException, Exceptions\AtolEmailValidateException};
/**
* Добавляет объекту функционал для работы с email
*
* @package AtolOnline\Traits
*/
trait HasEmail
{
/**
* @var string Почта
*/
protected $email;
/**
* Возвращает установленную почту. Тег ФФД: 1008, 1117.
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Устанавливает почту. Тег ФФД: 1008, 1117.
*
* @param string $email
* @return $this
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException Слишком длинный email
* @throws \AtolOnline\Exceptions\AtolEmailValidateException Невалидный email
*/
public function setEmail(string $email)
{
$email = trim($email);
if (valid_strlen($email) > Constraints::MAX_LENGTH_EMAIL) {
throw new AtolEmailTooLongException($email, Constraints::MAX_LENGTH_EMAIL);
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new AtolEmailValidateException($email);
}
$this->email = $email;
return $this;
}
}

View File

@@ -1,54 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Traits;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\AtolInnWrongLengthException;
/**
* Добавляет объекту функционал для работы с ИНН
*
* @package AtolOnline\Traits
*/
trait HasInn
{
/**
* @var string ИНН
*/
protected $inn;
/**
* Возвращает установленный ИНН. Тег ФФД: 1228, 1018.
*
* @return string
*/
public function getInn()
{
return $this->inn ?? '';
}
/**
* Устанавливает ИНН. Тег ФФД: 1228, 1018.
* Входная строка лишается всех знаков, кроме цифр.
*
* @param string $inn
* @return $this
* @throws AtolInnWrongLengthException Некорректная длина ИНН
*/
public function setInn(string $inn)
{
$inn = preg_replace("/[^0-9]/", '', $inn);
if (preg_match_all(Constraints::PATTERN_INN, $inn) == 0) {
throw new AtolInnWrongLengthException($inn);
}
$this->inn = $inn;
return $this;
}
}

View File

@@ -1,40 +0,0 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Traits;
/**
* Свойство класса, позволяющее конвертировать рубли <-> копейки
*
* @package AtolOnline\Traits
*/
trait RublesKopeksConverter
{
/**
* Конвертирует рубли в копейки, учитывая только 2 знака после запятой
*
* @param float|null $rubles Рубли
* @return int Копейки
*/
protected static function toKop(?float $rubles = null)
{
return $rubles === null ? null : (int)round($rubles * 100, 2);
}
/**
* Конвертирует копейки в рубли, оставляя только 2 знака после запятой
*
* @param int|null $kopeks Копейки
* @return float Рубли
*/
protected static function toRub(?int $kopeks = null)
{
return $kopeks === null ? null : round($kopeks / 100, 2);
}
}

View File

@@ -0,0 +1,146 @@
<?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\Constants;
/**
* Класс с константами ограничений
*/
final class Constraints
{
/**
* Максимальная длина Callback URL
*/
const MAX_LENGTH_CALLBACK_URL = 256;
/**
* Максимальная длина email
*/
const MAX_LENGTH_EMAIL = 64;
/**
* Максимальная длина логина ККТ
*/
const MAX_LENGTH_LOGIN = 100;
/**
* Максимальная длина пароля ККТ
*/
const MAX_LENGTH_PASSWORD = 100;
/**
* Максимальная длина адреса места расчётов
*/
const MAX_LENGTH_PAYMENT_ADDRESS = 256;
/**
* Максимальная длина наименования покупателя (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;
/**
* Максимальная стоимость всех предметов расчёта в документе прихода, расхода,
* возврата прихода, возврата расхода (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;
/**
* Максимальное количество платежей в любом документе
* @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;
/**
* Максимальная длина имени кассира (1021)
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32
*/
const MAX_LENGTH_CASHIER_NAME = 64;
/**
* Регулярное выражание для валидации строки ИНН
*
* @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/client/inn"
*/
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 */
'/^([^\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а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/';
}

View File

@@ -0,0 +1,149 @@
<?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\Constants;
/**
* Константы тегов ФФД 1.05
*/
final class Ffd105Tags
{
/**
* Телефон или электронный адрес покупателя
*/
const CLIENT_PHONE_EMAIL = 1008;
/**
* Наименование организации или фамилия, имя, отчество (при наличии), серия и номер паспорта покупателя (клиента)
*/
const CLIENT_NAME = 1227;
/**
* ИНН организации или покупателя (клиента)
*/
const CLIENT_INN = 1228;
/**
* Адрес электронной почты отправителя чека
*/
const COMPANY_EMAIL = 1117;
/**
* ИНН пользователя
*/
const COMPANY_INN = 1008;
/**
* Место расчётов
*/
const COMPANY_PADDRESS = 1187;
/**
* Телефон оператора по приёму платежей
*/
const RPO_PHONES = 1074;
/**
* Телефон оператора перевода
*/
const MTO_PHONES = 1075;
/**
* ИНН оператора перевода
*/
const MTO_INN = 1016;
/**
* Телефон платёжного агента
*/
const PAGENT_PHONE = 1073;
/**
* Телефон поставщика
*/
const SUPPLIER_PHONES = 1171;
/**
* Наименование поставщика
*/
const SUPPLIER_NAME = 1225;
/**
* ИНН поставщика
*/
const SUPPLIER_INN = 1226;
/**
* Кассир
*/
const CASHIER = 1021;
/**
* Наименование предмета расчёта
*/
const ITEM_NAME = 1030;
/**
* Цена за единицу предмета расчёта с учётом скидок и наценок
*/
const ITEM_PRICE = 1079;
/**
* Количество предмета расчёта
*/
const ITEM_QUANTITY = 1023;
/**
* Стоимость предмета расчёта с учётом скидок и наценок
*/
const ITEM_SUM = 1043;
/**
* Единица измерения предмета расчёта
*/
const ITEM_MEASUREMENT_UNIT = 1197;
/**
* Код товара
*/
const ITEM_NOMENCLATURE_CODE = 1162;
/**
* Признак способа расчёта
*/
const ITEM_PAYMENT_METHOD = 1214;
/**
* Признак предмета расчёта
*/
const ITEM_PAYMENT_OBJECT = 1212;
/**
* Дополнительный реквизит предмета расчёта
*/
const ITEM_USERDATA = 1191;
/**
* Сумма акциза с учётом копеек, включённая в стоимость предмета расчёта
*/
const ITEM_PAYMENT_EXSICE = 1229;
/**
* Цифровой код страны происхождения товара в соответствии с Общероссийским классификатором стран мира
*
* @see https://ru.wikipedia.org/wiki/Общероссийский_классификатор_стран_мира
* @see https://classifikators.ru/oksm
*/
const ITEM_COUNTRY_CODE = 1230;
/**
* Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155)
*/
const DECLARATION_NUMBER = 1231;
}

169
src/Entities/AgentInfo.php Normal file
View File

@@ -0,0 +1,169 @@
<?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\Entities;
use AtolOnline\Enums\AgentTypes;
use AtolOnline\Exceptions\InvalidEnumValueException;
/**
* Класс, описывающий данные агента
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 26-28
*/
class AgentInfo extends Entity
{
/**
* @var string|null Признак агента (1057)
*/
protected ?string $type = null;
/**
* @var PayingAgent|null Платёжный агент
*/
protected ?PayingAgent $paying_agent = null;
/**
* @var ReceivePaymentsOperator|null Оператор по приёму платежей
*/
protected ?ReceivePaymentsOperator $receive_payments_operator = null;
/**
* @var MoneyTransferOperator|null Оператор перевода
*/
protected ?MoneyTransferOperator $money_transfer_operator = null;
/**
* Конструктор
*
* @param string|null $type Признак агента (1057)
* @param PayingAgent|null $paying_agent Платёжный агент
* @param ReceivePaymentsOperator|null $receive_payments_operator Оператор по приёму платежей
* @param MoneyTransferOperator|null $money_transfer_operator Оператор перевода
* @throws InvalidEnumValueException
*/
public function __construct(
?string $type = null,
?PayingAgent $paying_agent = null,
?ReceivePaymentsOperator $receive_payments_operator = null,
?MoneyTransferOperator $money_transfer_operator = null,
) {
!is_null($type) && $this->setType($type);
!is_null($paying_agent) && $this->setPayingAgent($paying_agent);
!is_null($receive_payments_operator) && $this->setReceivePaymentsOperator($receive_payments_operator);
!is_null($money_transfer_operator) && $this->setMoneyTransferOperator($money_transfer_operator);
}
/**
* Возвращает установленный признак оператора
*
* @return string|null
*/
public function getType(): ?string
{
return $this->type;
}
/**
* Устанавливает признак оператора
*
* @param string|null $type
* @return AgentInfo
* @throws InvalidEnumValueException
*/
public function setType(?string $type): AgentInfo
{
AgentTypes::isValid($type) && $this->type = $type;
return $this;
}
/**
* Взвращает установленного платёжного агента
*
* @return PayingAgent|null
*/
public function getPayingAgent(): ?PayingAgent
{
return $this->paying_agent;
}
/**
* Устанавливает платёжного агента
*
* @param PayingAgent|null $paying_agent
* @return AgentInfo
*/
public function setPayingAgent(?PayingAgent $paying_agent): AgentInfo
{
$this->paying_agent = $paying_agent;
return $this;
}
/**
* Возвращает установленного оператора по приёму платежей
*
* @return ReceivePaymentsOperator|null
*/
public function getReceivePaymentsOperator(): ?ReceivePaymentsOperator
{
return $this->receive_payments_operator;
}
/**
* Устанавливает оператора по приёму платежей
*
* @param ReceivePaymentsOperator|null $receive_payments_operator
* @return AgentInfo
*/
public function setReceivePaymentsOperator(?ReceivePaymentsOperator $receive_payments_operator): AgentInfo
{
$this->receive_payments_operator = $receive_payments_operator;
return $this;
}
/**
* Возвращает установленного оператора перевода
*
* @return MoneyTransferOperator|null
*/
public function getMoneyTransferOperator(): ?MoneyTransferOperator
{
return $this->money_transfer_operator;
}
/**
* Устанавливает оператора перевода
*
* @param MoneyTransferOperator|null $money_transfer_operator
* @return AgentInfo
*/
public function setMoneyTransferOperator(?MoneyTransferOperator $money_transfer_operator): AgentInfo
{
$this->money_transfer_operator = $money_transfer_operator;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
$json = [];
$this->getType() && $json['type'] = $this->getType();
$this->getPayingAgent()?->jsonSerialize() && $json['paying_agent'] = $this
->getPayingAgent()->jsonSerialize();
$this->getReceivePaymentsOperator()?->jsonSerialize() && $json['receive_payments_operator'] = $this
->getReceivePaymentsOperator()->jsonSerialize();
$this->getMoneyTransferOperator()?->jsonSerialize() && $json['money_transfer_operator'] = $this
->getMoneyTransferOperator()->jsonSerialize();
return $json;
}
}

206
src/Entities/Client.php Normal file
View File

@@ -0,0 +1,206 @@
<?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\Entities;
use AtolOnline\{
Constants\Constraints,
Exceptions\InvalidEmailException,
Exceptions\InvalidInnLengthException,
Exceptions\TooLongClientContactException,
Exceptions\TooLongClientNameException,
Exceptions\TooLongEmailException};
/**
* Класс Client, описывающий сущность покупателя
*
* @package AtolOnline\Entities
*/
class Client extends Entity
{
/**
* @var string|null Наименование (1227)
*/
protected ?string $name = null;
/**
* @var string|null Email (1008)
*/
protected ?string $email = null;
/**
* @var string|null Телефон (1008)
*/
protected ?string $phone = null;
/**
* @var string|null ИНН (1228)
*/
protected ?string $inn = null;
/**
* Конструктор объекта покупателя
*
* @param string|null $name Наименование (1227)
* @param string|null $phone Email (1008)
* @param string|null $email Телефон (1008)
* @param string|null $inn ИНН (1228)
* @throws TooLongClientNameException
* @throws TooLongClientContactException
* @throws TooLongEmailException
* @throws InvalidEmailException
* @throws InvalidInnLengthException
*/
public function __construct(
?string $name = null,
?string $email = null,
?string $phone = null,
?string $inn = null
) {
!is_null($name) && $this->setName($name);
!is_null($email) && $this->setEmail($email);
!is_null($phone) && $this->setPhone($phone);
!is_null($inn) && $this->setInn($inn);
}
/**
* Возвращает наименование покупателя
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Устанавливает наименование покупателя
*
* @todo улучшить валидацию по Constraints::PATTERN_PHONE
* @param string|null $name
* @return $this
* @throws TooLongClientNameException
*/
public function setName(?string $name): self
{
if (is_string($name)) {
$name = preg_replace('/[\n\r\t]/', '', trim($name));
if (mb_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) {
throw new TooLongClientNameException($name);
}
}
$this->name = empty($name) ? null : $name;
return $this;
}
/**
* Возвращает установленный email
*
* @return string|null
*/
public function getEmail(): ?string
{
return $this->email;
}
/**
* Устанавливает email
*
* @param string|null $email
* @return $this
* @throws TooLongEmailException Слишком длинный email
* @throws InvalidEmailException Невалидный email
*/
public function setEmail(?string $email): self
{
if (is_string($email)) {
$email = preg_replace('/[\n\r\t]/', '', trim($email));
if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) {
throw new TooLongEmailException($email);
} elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidEmailException($email);
}
}
$this->email = empty($email) ? null : $email;
return $this;
}
/**
* Возвращает установленный телефон
*
* @return string|null
*/
public function getPhone(): ?string
{
return $this->phone;
}
/**
* Устанавливает телефон
*
* @param string|null $phone Номер телефона
* @return $this
* @throws TooLongClientContactException
*/
public function setPhone(?string $phone): self
{
if (is_string($phone)) {
$phone = preg_replace('/[^\d]/', '', trim($phone));
if (mb_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_CONTACT) {
throw new TooLongClientContactException($phone);
}
}
$this->phone = empty($phone) ? null : "+$phone";
return $this;
}
/**
* Возвращает установленный ИНН
*
* @return string|null
*/
public function getInn(): ?string
{
return $this->inn;
}
/**
* Устанавливает ИНН
*
* @param string|null $inn
* @return $this
* @throws InvalidInnLengthException Некорректная длина ИНН
*/
public function setInn(?string $inn): self
{
if (is_string($inn)) {
$inn = preg_replace('/[^\d]/', '', trim($inn));
if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) {
throw new InvalidInnLengthException($inn);
}
}
$this->inn = empty($inn) ? null : $inn;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
$json = [];
$this->getName() && $json['name'] = $this->getName();
$this->getEmail() && $json['email'] = $this->getEmail();
$this->getPhone() && $json['phone'] = $this->getPhone();
$this->getInn() && $json['inn'] = $this->getInn();
return $json;
}
}

213
src/Entities/Company.php Normal file
View File

@@ -0,0 +1,213 @@
<?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\Entities;
use AtolOnline\{
Constants\Constraints,
Enums\SnoTypes,
Exceptions\InvalidEmailException,
Exceptions\InvalidEnumValueException,
Exceptions\InvalidInnLengthException,
Exceptions\InvalidPaymentAddressException,
Exceptions\TooLongEmailException,
Exceptions\TooLongPaymentAddressException};
/**
* Класс, описывающий сущность компании-продавца
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
*/
class Company extends Entity
{
/**
* @var string|null Почта (1117)
*/
protected ?string $email;
/**
* @var string|null Система налогообложения продавца (1055)
*/
protected ?string $sno;
/**
* @var string|null ИНН (1018)
*/
protected ?string $inn;
/**
* @var string|null Место расчётов (адрес интернет-магазина) (1187)
*/
protected ?string $payment_address;
/**
* Конструктор
*
* @param string $sno Система налогообложения продавца (1055)
* @param string $inn ИНН (1018)
* @param string $payment_address Место расчётов (адрес интернет-магазина) (1187)
* @param string $email Почта (1117)
* @throws InvalidEmailException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws InvalidEnumValueException
* @throws TooLongEmailException
* @throws TooLongPaymentAddressException
*/
public function __construct(
string $email,
string $sno,
string $inn,
string $payment_address,
) {
$this->setEmail($email);
$this->setSno($sno);
$this->setInn($inn);
$this->setPaymentAddress($payment_address);
}
/**
* Возвращает установленный email
*
* @return string
*/
public function getEmail(): string
{
return $this->email;
}
/**
* Устанавливает email
*
* @param string $email
* @return $this
* @throws TooLongEmailException Слишком длинный email
* @throws InvalidEmailException Невалидный email
*/
public function setEmail(string $email): self
{
$email = preg_replace('/[\n\r\t]/', '', trim($email));
if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) {
throw new TooLongEmailException($email);
} elseif (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidEmailException($email);
}
$this->email = $email;
return $this;
}
/**
* Возвращает установленный тип налогообложения
*
* @return string
*/
public function getSno(): string
{
return $this->sno;
}
/**
* Устанавливает тип налогообложения
*
* @param string $sno
* @return $this
* @throws InvalidEnumValueException
*/
public function setSno(string $sno): self
{
$sno = trim($sno);
SnoTypes::isValid($sno);
$this->sno = $sno;
return $this;
}
/**
* Возвращает установленный ИНН
*
* @return string
*/
public function getInn(): string
{
return $this->inn;
}
/**
* Устанавливает ИНН
*
* @param string $inn
* @return $this
* @throws InvalidInnLengthException
*/
public function setInn(string $inn): self
{
$inn = preg_replace('/[^\d]/', '', trim($inn));
if (empty($inn) || preg_match_all(Constraints::PATTERN_INN, $inn) === 0) {
throw new InvalidInnLengthException($inn);
}
$this->inn = $inn;
return $this;
}
/**
* Возвращает установленный адрес места расчётов
*
* @return string
*/
public function getPaymentAddress(): string
{
return $this->payment_address;
}
/**
* Устанавливает адрес места расчётов
*
* @param string $payment_address
* @return $this
* @throws TooLongPaymentAddressException
* @throws InvalidPaymentAddressException
*/
public function setPaymentAddress(string $payment_address): self
{
$payment_address = trim($payment_address);
if (empty($payment_address)) {
throw new InvalidPaymentAddressException();
} elseif (mb_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) {
throw new TooLongPaymentAddressException($payment_address);
}
$this->payment_address = $payment_address;
return $this;
}
/**
* @inheritDoc
* @throws InvalidEmailException
* @throws InvalidEnumValueException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
*/
public function jsonSerialize(): array
{
return [
'email' => $this->email
? $this->getEmail()
: throw new InvalidEmailException(),
'sno' => $this->sno
? $this->getSno()
: throw new InvalidEnumValueException(SnoTypes::class, 'null'),
'inn' => $this->inn
? $this->getInn()
: throw new InvalidInnLengthException(),
'payment_address' => $this->payment_address
? $this->getPaymentAddress()
: throw new InvalidPaymentAddressException(),
];
}
}

View File

@@ -1,65 +1,53 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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\Entities;
/**
* Класс CorrectionInfo, описывающий данные коррекции
*
* @package AtolOnline\Entities
* Класс CorrectionInfo, описывающий данные чек коррекции
*/
class CorrectionInfo extends Entity
{
/**
* @var int Тип коррекции. Тег ФФД - 1173.
* @var string Тип коррекции. Тег ФФД - 1173.
*/
protected $type;
protected string $type;
/**
* @var string Дата документа основания для коррекции. Тег ФФД - 1178.
*/
protected $base_date;
protected string $base_date;
/**
* @var string Номер документа основания для коррекции. Тег ФФД - 1179.
*/
protected $base_number;
/**
* @var string Описание коррекции. Тег ФФД - 1177.
*/
protected $base_name;
protected string $base_number;
/**
* CorrectionInfo constructor.
*
* @param string|null $type Тип коррекции
* @param string|null $base_date Дата документа
* @param string|null $type Тип коррекции
* @param string|null $base_date Дата документа
* @param string|null $base_number Номер документа
* @param string|null $base_name Описание коррекции
*/
public function __construct(?string $type = null, ?string $base_date = null, ?string $base_number = null, ?string $base_name = null)
{
if ($type) {
$this->setType($type);
}
if ($base_date) {
$this->setDate($base_date);
}
if ($base_number) {
$this->setNumber($base_number);
}
if ($base_name) {
$this->setName($base_name);
}
public function __construct(
?string $type = null,
?string $base_date = null,
?string $base_number = null
) {
$type && $this->setType($type);
$base_date && $this->setDate($base_date);
$base_number && $this->setNumber($base_number);
}
/**
* Возвращает номер документа основания для коррекции.
* Тег ФФД - 1179.
@@ -68,9 +56,9 @@ class CorrectionInfo extends Entity
*/
public function getNumber(): ?string
{
return $this->base_name;
return $this->base_number;
}
/**
* Устанавливает номер документа основания для коррекции.
* Тег ФФД - 1179.
@@ -78,36 +66,12 @@ class CorrectionInfo extends Entity
* @param string $number
* @return $this
*/
public function setNumber(string $number)
public function setNumber(string $number): CorrectionInfo
{
$this->base_number = trim($number);
return $this;
}
/**
* Возвращает описание коррекции.
* Тег ФФД - 1177.
*
* @return string|null
*/
public function getName(): ?string
{
return $this->base_name;
}
/**
* Устанавливает описание коррекции.
* Тег ФФД - 1177.
*
* @param string $name
* @return $this
*/
public function setName(string $name)
{
$this->base_name = trim($name);
return $this;
}
/**
* Возвращает дату документа основания для коррекции.
* Тег ФФД - 1178.
@@ -118,7 +82,7 @@ class CorrectionInfo extends Entity
{
return $this->base_date;
}
/**
* Устанавливает дату документа основания для коррекции.
* Тег ФФД - 1178.
@@ -126,12 +90,12 @@ class CorrectionInfo extends Entity
* @param string $date Строка в формате d.m.Y
* @return $this
*/
public function setDate(string $date)
public function setDate(string $date): CorrectionInfo
{
$this->base_date = $date;
return $this;
}
/**
* Возвращает тип коррекции.
* Тег ФФД - 1173.
@@ -142,7 +106,7 @@ class CorrectionInfo extends Entity
{
return $this->type;
}
/**
* Устанавливает тип коррекции.
* Тег ФФД - 1173.
@@ -150,22 +114,21 @@ class CorrectionInfo extends Entity
* @param string $type
* @return $this
*/
public function setType(string $type)
public function setType(string $type): CorrectionInfo
{
$this->type = $type;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize()
public function jsonSerialize(): array
{
return [
'type' => $this->getType() ?? '', // обязателен
'base_date' => $this->getDate() ?? '', // обязателен
'base_number' => $this->getNumber() ?? '', // обязателен
'base_name' => $this->getName() ?? '' // обязателен
'type' => $this->getType() ?? '',
'base_date' => $this->getDate() ?? '',
'base_number' => $this->getNumber() ?? '',
];
}
}

View File

@@ -1,18 +1,34 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\AtolCashierTooLongException;
use AtolOnline\Exceptions\AtolException;
use AtolOnline\Exceptions\AtolInvalidJsonException;
use AtolOnline\Exceptions\InvalidEmailException;
use AtolOnline\Exceptions\InvalidInnLengthException;
use AtolOnline\Exceptions\InvalidJsonException;
use AtolOnline\Exceptions\TooHighPriceException;
use AtolOnline\Exceptions\TooLongCashierException;
use AtolOnline\Exceptions\TooLongClientContactException;
use AtolOnline\Exceptions\TooLongEmailException;
use AtolOnline\Exceptions\TooLongItemNameException;
use AtolOnline\Exceptions\TooLongPaymentAddressException;
use AtolOnline\Exceptions\TooLongUnitException;
use AtolOnline\Exceptions\TooLongUserdataException;
use AtolOnline\Exceptions\TooManyException;
use AtolOnline\Exceptions\TooManyItemsException;
use AtolOnline\Exceptions\TooManyPaymentsException;
use AtolOnline\Exceptions\TooManyVatsException;
use Exception;
/**
* Класс, описывающий документ
@@ -22,45 +38,45 @@ use AtolOnline\Exceptions\AtolInvalidJsonException;
class Document extends Entity
{
/**
* @var \AtolOnline\Entities\ItemArray Массив предметов расчёта
* @var ItemArray Массив предметов расчёта
*/
protected $items;
protected ItemArray $items;
/**
* @var \AtolOnline\Entities\VatArray Массив ставок НДС
* @var VatArray Массив ставок НДС
*/
protected $vats;
protected VatArray $vats;
/**
* @var \AtolOnline\Entities\PaymentArray Массив оплат
* @var PaymentArray Массив оплат
*/
protected $payments;
protected PaymentArray $payments;
/**
* @var \AtolOnline\Entities\Company Объект компании (продавца)
* @var Company Объект компании (продавца)
*/
protected $company;
protected Company $company;
/**
* @var \AtolOnline\Entities\Client Объект клиента (покупателя)
* @var Client Объект клиента (покупателя)
*/
protected $client;
protected Client $client;
/**
* @var int Итоговая сумма чека. Тег ФФД - 1020.
* @var float Итоговая сумма чека. Тег ФФД - 1020.
*/
protected $total = 0;
protected float $total = 0;
/**
* @var string ФИО кассира. Тег ФФД - 1021.
*/
protected $cashier;
protected string $cashier;
/**
* @var \AtolOnline\Entities\CorrectionInfo Данные коррекции
* @var CorrectionInfo Данные коррекции
*/
protected $correction_info;
protected CorrectionInfo $correction_info;
/**
* Document constructor.
*/
@@ -70,66 +86,65 @@ class Document extends Entity
$this->payments = new PaymentArray();
$this->items = new ItemArray();
}
/**
* Удаляет все налоги из документа и предметов расчёта
*
* @return $this
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function clearVats()
public function clearVats(): Document
{
$this->setVats([]);
return $this;
}
/**
* Добавляет новую ставку НДС в массив ставок НДС
*
* @param \AtolOnline\Entities\Vat $vat Объект ставки НДС
* @param Vat $vat Объект ставки НДС
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function addVat(Vat $vat)
public function addVat(Vat $vat): Document
{
$this->vats->add($vat);
return $this;
}
/**
* Возвращает массив ставок НДС
*
* @return \AtolOnline\Entities\Vat[]
* @return Vat[]
*/
public function getVats(): array
{
return $this->vats->get();
}
/**
* Устанавливает массив ставок НДС
*
* @param \AtolOnline\Entities\Vat[] $vats Массив ставок НДС
* @param Vat[] $vats Массив ставок НДС
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \Exception
* @throws TooManyVatsException Слишком много ставок НДС
* @throws Exception
*/
public function setVats(array $vats)
public function setVats(array $vats): Document
{
$this->vats->set($vats);
return $this;
}
/**
* Добавляет новую оплату в массив оплат
*
* @param \AtolOnline\Entities\Payment $payment Объект оплаты
* @param Payment $payment Объект оплаты
* @return $this
* @throws \Exception
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат
* @throws Exception
* @throws TooManyPaymentsException Слишком много оплат
*/
public function addPayment(Payment $payment)
public function addPayment(Payment $payment): Document
{
if (count($this->getPayments()) == 0 && !$payment->getSum()) {
$payment->setSum($this->calcTotal());
@@ -137,66 +152,66 @@ class Document extends Entity
$this->payments->add($payment);
return $this;
}
/**
* Возвращает массив оплат
*
* @return \AtolOnline\Entities\Payment[]
* @return Payment[]
*/
public function getPayments(): array
{
return $this->payments->get();
}
/**
* Устанавливает массив оплат
*
* @param \AtolOnline\Entities\Payment[] $payments Массив оплат
* @param Payment[] $payments Массив оплат
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат
* @throws TooManyPaymentsException Слишком много оплат
*/
public function setPayments(array $payments)
public function setPayments(array $payments): Document
{
$this->payments->set($payments);
return $this;
}
/**
* Добавляет новый предмет расчёта в массив предметов расчёта
*
* @param \AtolOnline\Entities\Item $item Объект предмета расчёта
* @param Item $item Объект предмета расчёта
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function addItem(Item $item)
public function addItem(Item $item): Document
{
$this->items->add($item);
return $this;
}
/**
* Возвращает массив предметов расчёта
*
* @return \AtolOnline\Entities\Item[]
* @return Item[]
*/
public function getItems(): array
{
return $this->items->get();
}
/**
* Устанавливает массив предметов расчёта
*
* @param \AtolOnline\Entities\Item[] $items Массив предметов расчёта
* @param Item[] $items Массив предметов расчёта
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function setItems(array $items)
public function setItems(array $items): Document
{
$this->items->set($items);
return $this;
}
/**
* Возвращает заданного клиента (покупателя)
*
@@ -206,19 +221,19 @@ class Document extends Entity
{
return $this->client;
}
/**
* Устанавливает клиента (покупателя)
*
* @param Client|null $client
* @return $this
*/
public function setClient(?Client $client)
public function setClient(?Client $client): Document
{
$this->client = $client;
return $this;
}
/**
* Возвращает заданную компанию (продавца)
*
@@ -228,19 +243,19 @@ class Document extends Entity
{
return $this->company;
}
/**
* Устанавливает компанию (продавца)
*
* @param Company|null $company
* @return $this
*/
public function setCompany(?Company $company)
public function setCompany(?Company $company): Document
{
$this->company = $company;
return $this;
}
/**
* Возвращает ФИО кассира. Тег ФФД - 1021.
*
@@ -250,55 +265,55 @@ class Document extends Entity
{
return $this->cashier;
}
/**
* Устанавливает ФИО кассира. Тег ФФД - 1021.
*
* @param string|null $cashier
* @return $this
* @throws \AtolOnline\Exceptions\AtolCashierTooLongException
* @throws TooLongCashierException
*/
public function setCashier(?string $cashier)
public function setCashier(?string $cashier): Document
{
if ($cashier !== null) {
$cashier = trim($cashier);
if (valid_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
throw new AtolCashierTooLongException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME);
if (mb_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
throw new TooLongCashierException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME);
}
}
$this->cashier = $cashier;
return $this;
}
/**
* Возвращает данные коррекции
*
* @return \AtolOnline\Entities\CorrectionInfo|null
* @return CorrectionInfo|null
*/
public function getCorrectionInfo(): ?CorrectionInfo
{
return $this->correction_info;
}
/**
* Устанавливает данные коррекции
*
* @param \AtolOnline\Entities\CorrectionInfo|null $correction_info
* @param CorrectionInfo|null $correction_info
* @return $this
*/
public function setCorrectionInfo(?CorrectionInfo $correction_info)
public function setCorrectionInfo(?CorrectionInfo $correction_info): Document
{
$this->correction_info = $correction_info;
return $this;
}
/**
* Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020.
*
* @return float
* @throws \Exception
* @throws Exception
*/
public function calcTotal()
public function calcTotal(): float
{
$sum = 0;
$this->clearVats();
@@ -308,7 +323,7 @@ class Document extends Entity
}
return $this->total = round($sum, 2);
}
/**
* Возвращает итоговую сумму чека. Тег ФФД - 1020.
*
@@ -318,32 +333,33 @@ class Document extends Entity
{
return $this->total;
}
/**
* Собирает объект документа из сырой json-строки
*
* @param string $json
* @return \AtolOnline\Entities\Document
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException
* @throws \AtolOnline\Exceptions\AtolEmailValidateException
* @throws \AtolOnline\Exceptions\AtolException
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException
* @throws \AtolOnline\Exceptions\AtolInvalidJsonException
* @throws \AtolOnline\Exceptions\AtolNameTooLongException
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException
* @throws \AtolOnline\Exceptions\AtolPhoneTooLongException
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException
* @throws \AtolOnline\Exceptions\AtolTooManyException
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException
* @throws \AtolOnline\Exceptions\AtolUnitTooLongException
* @throws \AtolOnline\Exceptions\AtolUserdataTooLongException
* @return Document
* @throws TooLongEmailException
* @throws InvalidEmailException
* @throws AtolException
* @throws InvalidInnLengthException
* @throws InvalidJsonException
* @throws TooLongItemNameException
* @throws TooLongPaymentAddressException
* @throws TooLongClientContactException
* @throws TooHighPriceException
* @throws TooManyException
* @throws TooManyItemsException
* @throws TooManyPaymentsException
* @throws TooLongUnitException
* @throws TooLongUserdataException
* @throws Exception
*/
public static function fromRaw(string $json)
public static function fromRaw(string $json): Document
{
$array = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new AtolInvalidJsonException();
throw new InvalidJsonException();
}
$doc = new self();
if (isset($array['company'])) {
@@ -362,6 +378,14 @@ class Document extends Entity
$array['client']['inn'] ?? null
));
}
if (isset($array['correction_info'])) {
$doc->setCorrectionInfo(new CorrectionInfo(
$array['correction_info']['type'] ?? null,
$array['correction_info']['base_date'] ?? null,
$array['correction_info']['base_number'] ?? null,
$array['correction_info']['base_name'] ?? null,
));
}
if (isset($array['items'])) {
foreach ($array['items'] as $ar_item) {
$item = new Item(
@@ -391,18 +415,30 @@ class Document extends Entity
$doc->payments->add($payment);
}
}
if (isset($array['vats'])) {
foreach ($array['vats'] as $vat_payment) {
$vat = new Vat();
if (isset($vat_payment['type'])) {
$vat->setType($vat_payment['type']);
}
if (isset($vat_payment['sum'])) {
$vat->setSum($vat_payment['sum']);
}
$doc->vats->add($vat);
}
}
if (isset($array['total']) && $array['total'] != $doc->calcTotal()) {
throw new AtolException('Real total sum not equals to provided in JSON one');
}
return $doc;
}
/**
* Возвращает массив для кодирования в json
*
* @throws \Exception
* @throws Exception
*/
public function jsonSerialize()
public function jsonSerialize(): array
{
if ($this->getCompany()) {
$json['company'] = $this->getCompany()->jsonSerialize(); // обязательно

View File

@@ -1,28 +1,31 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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\Entities;
use JsonSerializable;
use Stringable;
/**
* Абстрактное описание любой сущности, представляемой как JSON
*
* @package AtolOnline\Entities
* Абстрактное описание любой сущности, представляемой как json
*/
abstract class Entity implements JsonSerializable
abstract class Entity implements JsonSerializable, Stringable
{
/**
* @inheritDoc
* Возвращает строковое представление json-структуры объекта
*
* @return false|string
*/
public function __toString()
{
return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE);
}
}
}

View File

@@ -1,98 +1,97 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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\Entities;
use AtolOnline\{Constants\Constraints,
Exceptions\AtolNameTooLongException,
Exceptions\AtolPriceTooHighException,
Exceptions\AtolTooManyException,
Exceptions\AtolUnitTooLongException,
Exceptions\AtolUserdataTooLongException,
Traits\RublesKopeksConverter
};
use AtolOnline\{
Constants\Constraints,
Exceptions\TooHighPriceException,
Exceptions\TooLongItemNameException,
Exceptions\TooLongUnitException,
Exceptions\TooLongUserdataException,
Exceptions\TooManyException};
/**
* Предмет расчёта (товар, услуга)
*
* @package AtolOnline\Entities
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21-30
*/
class Item extends Entity
{
use RublesKopeksConverter;
/**
* @var string Наименование. Тег ФФД - 1030.
* @var string Наименование (1030)
*/
protected $name;
protected string $name;
/**
* @var int Цена в копейках (с учётом скидок и наценок). Тег ФФД - 1079.
* @var int Цена в копейках (с учётом скидок и наценок) (1079)
*/
protected $price = 0;
protected int $price = 0;
/**
* @var float Количество, вес. Тег ФФД - 1023.
* @var float Количество, вес (1023)
*/
protected $quantity = 0.0;
protected float $quantity = 0.0;
/**
* @var float Сумма в копейках. Тег ФФД - 1043.
* @var float Сумма в копейках (1043)
*/
protected $sum = 0;
protected float $sum = 0;
/**
* @var string Единица измерения количества. Тег ФФД - 1197.
* @var string Единица измерения количества (1197)
*/
protected $measurement_unit;
protected string $measurement_unit;
/**
* @var Vat Ставка НДС
* @var Vat|null Ставка НДС
*/
protected $vat;
protected ?Vat $vat;
/**
* @var string Признак способа расчёта. Тег ФФД - 1214.
* @var string Признак способа расчёта (1214)
*/
protected $payment_method;
protected string $payment_method;
/**
* @var string Признак объекта расчёта. Тег ФФД - 1212.
* @var string Признак объекта расчёта (1212)
*/
protected $payment_object;
protected string $payment_object;
/**
* @var string Дополнительный реквизит. Тег ФФД - 1191.
* @var string Дополнительный реквизит (1191)
*/
protected $user_data;
protected string $user_data;
/**
* Item constructor.
*
* @param string|null $name Наименование
* @param float|null $price Цена за одну единицу
* @param float|null $quantity Количество
* @param string|null $name Наименование
* @param float|null $price Цена за одну единицу
* @param float|null $quantity Количество
* @param string|null $measurement_unit Единица измерения
* @param string|null $vat_type Ставка НДС
* @param string|null $payment_object Признак
* @param string|null $payment_method Способ расчёта
* @throws AtolNameTooLongException Слишком длинное наименование
* @throws AtolPriceTooHighException Слишком высокая цена за одну единицу
* @throws AtolTooManyException Слишком большое количество
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
* @param string|null $vat_type Ставка НДС
* @param string|null $payment_object Признак
* @param string|null $payment_method Способ расчёта
* @throws TooLongItemNameException Слишком длинное наименование
* @throws TooHighPriceException Слишком высокая цена за одну единицу
* @throws TooManyException Слишком большое количество
* @throws TooLongUnitException Слишком длинное название единицы измерения
*/
public function __construct(
?string $name = null,
?float $price = null,
?float $quantity = null,
?string $measurement_unit = null,
$vat_type = null,
?string $vat_type = null,
?string $payment_object = null,
?string $payment_method = null
) {
@@ -118,61 +117,61 @@ class Item extends Entity
$this->setPaymentMethod($payment_method);
}
}
/**
* Возвращает наименование. Тег ФФД - 1030.
*
* @return string
*/
public function getName()
public function getName(): string
{
return $this->name;
}
/**
* Устаналивает наименование. Тег ФФД - 1030.
*
* @param string $name Наименование
* @return $this
* @throws AtolNameTooLongException Слишком длинное имя/наименование
* @throws TooLongItemNameException Слишком длинное имя/наименование
*/
public function setName(string $name)
public function setName(string $name): self
{
$name = trim($name);
if (valid_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) {
throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_ITEM_NAME);
if (mb_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) {
throw new TooLongItemNameException($name, Constraints::MAX_LENGTH_ITEM_NAME);
}
$this->name = $name;
return $this;
}
/**
* Возвращает цену в рублях. Тег ФФД - 1079.
*
* @return float
*/
public function getPrice()
public function getPrice(): float
{
return self::toRub($this->price);
return rubles($this->price);
}
/**
* Устанавливает цену в рублях. Тег ФФД - 1079.
*
* @param float $rubles Цена за одну единицу в рублях
* @return $this
* @throws AtolPriceTooHighException Слишком высокая цена за одну единицу
* @throws TooHighPriceException Слишком высокая цена за одну единицу
*/
public function setPrice(float $rubles)
public function setPrice(float $rubles): Item
{
if ($rubles > 42949672.95) {
throw new AtolPriceTooHighException($rubles, 42949672.95);
throw new TooHighPriceException($rubles, 42949672.95);
}
$this->price = self::toKop($rubles);
$this->price = kopeks($rubles);
$this->calcSum();
return $this;
}
/**
* Возвращает количество. Тег ФФД - 1023.
*
@@ -182,22 +181,22 @@ class Item extends Entity
{
return $this->quantity;
}
/**
* Устанавливает количество. Тег ФФД - 1023.
*
* @param float $quantity Количество
* @param float $quantity Количество
* @param string|null $measurement_unit Единица измерения количества
* @return $this
* @throws AtolTooManyException Слишком большое количество
* @throws AtolPriceTooHighException Слишком высокая общая стоимость
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
* @throws TooManyException Слишком большое количество
* @throws TooHighPriceException Слишком высокая общая стоимость
* @throws TooLongUnitException Слишком длинное название единицы измерения
*/
public function setQuantity(float $quantity, string $measurement_unit = null)
public function setQuantity(float $quantity, string $measurement_unit = null): Item
{
$quantity = round($quantity, 3);
if ($quantity > 99999.999) {
throw new AtolTooManyException($quantity, 99999.999);
throw new TooManyException($quantity, 99999.999);
}
$this->quantity = $quantity;
$this->calcSum();
@@ -206,7 +205,7 @@ class Item extends Entity
}
return $this;
}
/**
* Возвращает заданную единицу измерения количества. Тег ФФД - 1197.
*
@@ -216,24 +215,24 @@ class Item extends Entity
{
return $this->measurement_unit;
}
/**
* Устанавливает единицу измерения количества. Тег ФФД - 1197.
*
* @param string $measurement_unit Единица измерения количества
* @return $this
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
* @throws TooLongUnitException Слишком длинное название единицы измерения
*/
public function setMeasurementUnit(string $measurement_unit)
public function setMeasurementUnit(string $measurement_unit): Item
{
$measurement_unit = trim($measurement_unit);
if (valid_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) {
throw new AtolUnitTooLongException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT);
if (mb_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) {
throw new TooLongUnitException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT);
}
$this->measurement_unit = $measurement_unit;
return $this;
}
/**
* Возвращает признак способа оплаты. Тег ФФД - 1214.
*
@@ -243,7 +242,7 @@ class Item extends Entity
{
return $this->payment_method;
}
/**
* Устанавливает признак способа оплаты. Тег ФФД - 1214.
*
@@ -251,12 +250,12 @@ class Item extends Entity
* @return $this
* @todo Проверка допустимых значений
*/
public function setPaymentMethod(string $payment_method)
public function setPaymentMethod(string $payment_method): Item
{
$this->payment_method = trim($payment_method);
return $this;
}
/**
* Возвращает признак предмета расчёта. Тег ФФД - 1212.
*
@@ -266,7 +265,7 @@ class Item extends Entity
{
return $this->payment_object;
}
/**
* Устанавливает признак предмета расчёта. Тег ФФД - 1212.
*
@@ -274,30 +273,30 @@ class Item extends Entity
* @return $this
* @todo Проверка допустимых значений
*/
public function setPaymentObject(string $payment_object)
public function setPaymentObject(string $payment_object): Item
{
$this->payment_object = trim($payment_object);
return $this;
}
/**
* Возвращает ставку НДС
*
* @return \AtolOnline\Entities\Vat|null
* @return Vat|null
*/
public function getVat(): ?Vat
{
return $this->vat;
}
/**
* Устанавливает ставку НДС
*
* @param string|null $vat_type Тип ставки НДС. Передать null, чтобы удалить ставку.
* @return $this
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException
* @throws TooHighPriceException
*/
public function setVatType(?string $vat_type)
public function setVatType(?string $vat_type): Item
{
if ($vat_type) {
$this->vat
@@ -309,7 +308,7 @@ class Item extends Entity
$this->calcSum();
return $this;
}
/**
* Возвращает дополнительный реквизит. Тег ФФД - 1191.
*
@@ -319,24 +318,24 @@ class Item extends Entity
{
return $this->user_data;
}
/**
* Устанавливает дополнительный реквизит. Тег ФФД - 1191.
*
* @param string $user_data Дополнительный реквизит. Тег ФФД - 1191.
* @return $this
* @throws AtolUserdataTooLongException Слишком длинный дополнительный реквизит
* @throws TooLongUserdataException Слишком длинный дополнительный реквизит
*/
public function setUserData(string $user_data)
public function setUserData(string $user_data): self
{
$user_data = trim($user_data);
if (valid_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) {
throw new AtolUserdataTooLongException($user_data, Constraints::MAX_LENGTH_USER_DATA);
if (mb_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) {
throw new TooLongUserdataException($user_data, Constraints::MAX_LENGTH_USER_DATA);
}
$this->user_data = $user_data;
return $this;
}
/**
* Возвращает стоимость. Тег ФФД - 1043.
*
@@ -344,28 +343,28 @@ class Item extends Entity
*/
public function getSum(): float
{
return self::toRub($this->sum);
return rubles($this->sum);
}
/**
* Расчитывает стоимость и размер НДС на неё
*
* @return float
* @throws AtolPriceTooHighException Слишком большая сумма
* @throws TooHighPriceException Слишком большая сумма
*/
public function calcSum()
public function calcSum(): float
{
$sum = $this->quantity * $this->price;
if (self::toRub($sum) > 42949672.95) {
throw new AtolPriceTooHighException($sum, 42949672.95);
if (rubles($sum) > 42949672.95) {
throw new TooHighPriceException($sum, 42949672.95);
}
$this->sum = $sum;
if ($this->vat) {
$this->vat->setSum(self::toRub($sum));
$this->vat->setSum(rubles($sum));
}
return $this->getSum();
}
/**
* @inheritDoc
*/

136
src/Entities/Kkt.php Normal file
View File

@@ -0,0 +1,136 @@
<?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\Entities;
use AtolOnline\Exceptions\EmptyMonitorDataException;
use AtolOnline\Exceptions\NotEnoughMonitorDataException;
use DateTime;
use Exception;
/**
* Класс сущности ККТ, получаемой от монитора
*
* @property string|null serialNumber Заводской номер ККТ
* @property string|null registrationNumber Регистрационный номер машины (РНМ)
* @property string|null deviceNumber Номер автоматического устройства (внутренний идентификатор устройства)
* @property DateTime|string|null fiscalizationDate Дата активации (фискализации) ФН с указанием таймзоны
* @property DateTime|string|null fiscalStorageExpiration Дата замены ФН (Срок действия ФН), с указанием таймзоны
* @property int|null signedDocuments Количество подписанных документов в ФН
* @property float|null fiscalStoragePercentageUse Наполненость ФН в %
* @property string|null fiscalStorageINN ИНН компании (указанный в ФН)
* @property string|null fiscalStorageSerialNumber Заводской (серийный) номер ФН
* @property string|null fiscalStoragePaymentAddress Адрес расчёта, указанный в ФН
* @property string|null groupCode Код группы кассы
* @property DateTime|string|null timestamp Время и дата формирования данных, UTC
* @property bool|null isShiftOpened Признак открыта смена (true) или закрыта (false)
* @property int|null shiftNumber Номер смены (или "Номер закрытой смены", когда смена закрыта)
* @property int|null shiftReceipt Номер документа за смену (или "Кол-во чеков закрытой смены", когда смена закрыта)
* @property int|null unsentDocs Количество неотправленных документов. Указывается, если значение отлично от 0.
* @property DateTime|string|null firstUnsetDocTimestamp Дата первого неотправленного документа. Указывается, если
* есть неотправленные документы.
* @property int|null networkErrorCode Код ошибки сети
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11
*/
final class Kkt extends Entity
{
/**
* Сопоставление кодов сетевых ошибок ККТ с их описаниями
*/
public const ERROR_CODES = [
0 => 'Нет ошибок',
1 => 'Отсутствует физический канал связи',
2 => 'Ошибка сетевых настроек или нет соединения с сервером ОФД',
3 => 'Разрыв соединения при передаче документа на сервер',
4 => 'Некорректный заголовок сессионного пакета',
5 => 'Превышен таймаут ожидания квитанции',
6 => 'Разрыв соединения при приёме квитанции',
7 => 'Превышен таймаут передачи документа на сервер',
8 => 'ОФД-процесс не иницилизирован',
];
/**
* @var string[] Список обязательных атрибутов
*/
private array $properties = [
'serialNumber',
'registrationNumber',
'deviceNumber',
'fiscalizationDate',
'fiscalStorageExpiration',
'signedDocuments',
'fiscalStoragePercentageUse',
'fiscalStorageINN',
'fiscalStorageSerialNumber',
'fiscalStoragePaymentAddress',
'groupCode',
'timestamp',
'isShiftOpened',
'shiftNumber',
'shiftReceipt',
//'unsentDocs',
//'firstUnsetDocTimestamp',
'networkErrorCode',
];
/**
* @var string[] Массив атрибутов, которые кастуются к DateTime
*/
private array $timestamps = [
'fiscalizationDate',
'fiscalStorageExpiration',
'firstUnsetDocTimestamp',
'timestamp',
];
/**
* Конструктор
*
* @throws EmptyMonitorDataException
* @throws NotEnoughMonitorDataException
*/
public function __construct(protected \stdClass $data)
{
if (empty((array)$data)) {
throw new EmptyMonitorDataException();
}
$diff = array_diff($this->properties, array_keys((array)$data));
if (count($diff) !== 0) {
throw new NotEnoughMonitorDataException($diff);
}
}
/**
* Эмулирует обращение к атрибутам
*
* @param string $name
* @return null
* @throws Exception
*/
public function __get(string $name)
{
if (empty($this->data?->$name)) {
return null;
}
if (in_array($name, $this->timestamps)) {
return new DateTime($this->data->$name);
}
return $this->data->$name;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
return (array)$this->data;
}
}

View File

@@ -0,0 +1,189 @@
<?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\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\InvalidInnLengthException;
use AtolOnline\Exceptions\InvalidPhoneException;
use Illuminate\Support\Collection;
/**
* Класс, описывающий оператора перевода
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 28
*/
class MoneyTransferOperator extends Entity
{
/**
* @var string|null Наименование (1026)
*/
protected ?string $name = null;
/**
* @var string|null ИНН (1016)
*/
protected ?string $inn = null;
/**
* @var string|null Адрес (1005)
*/
protected ?string $address = null;
/**
* @var Collection Телефоны (1075)
*/
protected Collection $phones;
/**
* Конструктор
*
* @param string|null $name Наименование поставщика (1225)
* @param string|null $inn ИНН (1226)
* @param string|null $address Адрес (1005)
* @param array|Collection|null $phones Телефоны поставщика (1171)
* @throws InvalidInnLengthException
* @throws InvalidPhoneException
*/
public function __construct(
?string $name = null,
?string $inn = null,
?string $address = null,
array|Collection|null $phones = null,
) {
$this->setName($name);
$this->setInn($inn);
$this->setAddress($address);
$this->setPhones($phones);
}
/**
* Возвращает установленное наименование поставщика
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Устанавливает наименование поставщика
*
* @param string|null $name
* @return $this
*/
public function setName(?string $name): self
{
// критерии валидной строки не описаны ни в схеме, ни в документации
$this->name = trim((string)$name) ?: null;
return $this;
}
/**
* Возвращает установленный адрес места расчётов
*
* @return string|null
*/
public function getAddress(): ?string
{
return $this->address;
}
/**
* Устанавливает адрес места расчётов
*
* @param string|null $address
* @return $this
*/
public function setAddress(?string $address): self
{
// критерии валидной строки не описаны ни в схеме, ни в документации
$this->address = trim((string)$address) ?: null;
return $this;
}
/**
* Возвращает установленные номера телефонов
*
* @todo вытащить в трейт
* @return Collection
*/
public function getPhones(): Collection
{
return $this->phones;
}
/**
* Устанавливает массив номеров телефонов
*
* @todo вытащить в трейт
* @param array|Collection|null $phones
* @return $this
* @throws InvalidPhoneException
*/
public function setPhones(array|Collection|null $phones): self
{
if (!is_null($phones)) {
$phones = is_array($phones) ? collect($phones) : $phones;
$phones->each(function ($phone) {
$phone = preg_replace('/[^\d]/', '', trim($phone));
if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) {
throw new InvalidPhoneException($phone);
}
});
}
$this->phones = $phones ?? collect();
return $this;
}
/**
* Возвращает установленный ИНН
*
* @return string|null
*/
public function getInn(): ?string
{
return $this->inn;
}
/**
* Устанавливает ИНН
*
* @param string|null $inn
* @return $this
* @throws InvalidInnLengthException Некорректная длина ИНН
*/
public function setInn(?string $inn): self
{
if (is_string($inn)) {
$inn = preg_replace('/[^\d]/', '', trim($inn));
if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) {
throw new InvalidInnLengthException($inn);
}
}
$this->inn = $inn ?: null;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
$json = [];
$this->getName() && $json['name'] = $this->getName();
$this->getInn() && $json['inn'] = $this->getInn();
$this->getAddress() && $json['address'] = $this->getAddress();
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
return $json;
}
}

View File

@@ -0,0 +1,125 @@
<?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\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\InvalidPhoneException;
use AtolOnline\Exceptions\TooLongPayingAgentOperationException;
use Illuminate\Support\Collection;
/**
* Класс, описывающий платёжного агента
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19
*/
class PayingAgent extends Entity
{
/**
* @var string|null Наименование операции (1044)
*/
protected ?string $operation = null;
/**
* @var Collection Телефоны платёжного агента (1073)
*/
protected Collection $phones;
/**
* Конструктор
*
* @param string|null $operation Наименование операции (1044)
* @param array|Collection|null $phones Телефоны платёжного агента (1073)
* @throws TooLongPayingAgentOperationException
* @throws InvalidPhoneException
*/
public function __construct(
?string $operation = null,
array|Collection|null $phones = null,
) {
!is_null($operation) && $this->setOperation($operation);
$this->setPhones($phones);
}
/**
* Устанавливает операцию
*
* @param string|null $operation
* @return $this
* @throws TooLongPayingAgentOperationException
*/
public function setOperation(?string $operation): self
{
if (!is_null($operation)) {
$operation = trim($operation);
if (mb_strlen($operation) > Constraints::MAX_LENGTH_PAYING_AGENT_OPERATION) {
throw new TooLongPayingAgentOperationException($operation);
}
}
$this->operation = empty($operation) ? null : $operation;
return $this;
}
/**
* Вoзвращает установленную операцию
*
* @return string|null
*/
public function getOperation(): ?string
{
return $this->operation;
}
/**
* Устанавливает массив номеров телефонов
*
* @todo вытащить в трейт
* @param array|Collection|null $phones
* @return $this
* @throws InvalidPhoneException
*/
public function setPhones(array|Collection|null $phones): self
{
if (!is_null($phones)) {
$phones = is_array($phones) ? collect($phones) : $phones;
$phones->each(function ($phone) {
$phone = preg_replace('/[^\d]/', '', trim($phone));
if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) {
throw new InvalidPhoneException($phone);
}
});
}
$this->phones = empty($phones) ? collect() : $phones;
return $this;
}
/**
* Возвращает установленные номера телефонов
*
* @todo вытащить в трейт
* @return Collection
*/
public function getPhones(): Collection
{
return $this->phones;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
$json = [];
$this->getOperation() && $json['operation'] = $this->getOperation();
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
return $json;
}
}

View File

@@ -1,15 +1,17 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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\Entities;
use AtolOnline\Constants\PaymentTypes;
use AtolOnline\Enums\PaymentTypes;
/**
* Класс, описывающий оплату. Тег ФФД - 1031, 1081, 1215, 1216, 1217.
@@ -21,12 +23,12 @@ class Payment extends Entity
/**
* @var int Тип оплаты
*/
protected $type;
protected int $type;
/**
* @var float Сумма оплаты
*/
protected $sum;
protected float $sum;
/**
* Payment constructor.
@@ -56,7 +58,7 @@ class Payment extends Entity
* @param int $type
* @return $this
*/
public function setType(int $type)
public function setType(int $type): Payment
{
$this->type = $type;
return $this;
@@ -78,7 +80,7 @@ class Payment extends Entity
* @param float $sum
* @return $this
*/
public function setSum(float $sum)
public function setSum(float $sum): Payment
{
$this->sum = $sum;
return $this;

View File

@@ -0,0 +1,85 @@
<?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\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\InvalidPhoneException;
use Illuminate\Support\Collection;
/**
* Класс, описывающий оператора по приёму платежей
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19-20
*/
class ReceivePaymentsOperator extends Entity
{
/**
* @var Collection Телефоны оператора по приёму платежей (1074)
*/
protected Collection $phones;
/**
* Конструктор
*
* @param array|Collection|null $phones Телефоны оператора по приёму платежей (1074)
* @throws InvalidPhoneException
*/
public function __construct(
array|Collection|null $phones = null,
) {
$this->setPhones($phones);
}
/**
* Возвращает установленные номера телефонов
*
* @todo вытащить в трейт
* @return Collection
*/
public function getPhones(): Collection
{
return $this->phones;
}
/**
* Устанавливает массив номеров телефонов
*
* @todo вытащить в трейт
* @param array|Collection|null $phones
* @return $this
* @throws InvalidPhoneException
*/
public function setPhones(array|Collection|null $phones): self
{
if (!is_null($phones)) {
$phones = is_array($phones) ? collect($phones) : $phones;
$phones->each(function ($phone) {
$phone = preg_replace('/[^\d]/', '', trim($phone));
if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) {
throw new InvalidPhoneException($phone);
}
});
}
$this->phones = empty($phones) ? collect() : $phones;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
$json = [];
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
return $json;
}
}

157
src/Entities/Supplier.php Normal file
View File

@@ -0,0 +1,157 @@
<?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\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\InvalidInnLengthException;
use AtolOnline\Exceptions\InvalidPhoneException;
use Illuminate\Support\Collection;
/**
* Класс, описывающий поставшика
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29
*/
class Supplier extends Entity
{
/**
* @var string|null Наименование (1225)
*/
protected ?string $name = null;
/**
* @var string|null ИНН (1226)
*/
protected ?string $inn = null;
/**
* @var Collection Телефоны (1171)
*/
protected Collection $phones;
/**
* Конструктор
*
* @param string|null $name Наименование поставщика (1225)
* @param string|null $inn ИНН (1226)
* @param array|Collection|null $phones Телефоны поставщика (1171)
* @throws InvalidInnLengthException
* @throws InvalidPhoneException
*/
public function __construct(
?string $name = null,
?string $inn = null,
array|Collection|null $phones = null,
) {
!is_null($name) && $this->setName($name);
!is_null($inn) && $this->setInn($inn);
$this->setPhones($phones);
}
/**
* Возвращает установленное наименование поставщика
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Устанавливает наименование поставщика
*
* @param string|null $name
* @return Supplier
*/
public function setName(?string $name): self
{
// критерии к длине строки не описаны ни в схеме, ни в документации
$this->name = trim($name) ?: null;
return $this;
}
/**
* Возвращает установленные номера телефонов
*
* @todo вытащить в трейт
* @return Collection
*/
public function getPhones(): Collection
{
return $this->phones;
}
/**
* Устанавливает массив номеров телефонов
*
* @todo вытащить в трейт
* @param array|Collection|null $phones
* @return $this
* @throws InvalidPhoneException
*/
public function setPhones(array|Collection|null $phones): self
{
if (!is_null($phones)) {
$phones = is_array($phones) ? collect($phones) : $phones;
$phones->each(function ($phone) {
$phone = preg_replace('/[^\d]/', '', trim($phone));
if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) {
throw new InvalidPhoneException($phone);
}
});
}
$this->phones = empty($phones) ? collect() : $phones;
return $this;
}
/**
* Возвращает установленный ИНН
*
* @return string|null
*/
public function getInn(): ?string
{
return $this->inn;
}
/**
* Устанавливает ИНН
*
* @param string|null $inn
* @return $this
* @throws InvalidInnLengthException Некорректная длина ИНН
*/
public function setInn(?string $inn): self
{
if (is_string($inn)) {
$inn = preg_replace('/[^\d]/', '', trim($inn));
if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) {
throw new InvalidInnLengthException($inn);
}
}
$this->inn = empty($inn) ? null : $inn;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
$json = [];
$this->getName() && $json['name'] = $this->getName();
$this->getInn() && $json['inn'] = $this->getInn();
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
return $json;
}
}

136
src/Entities/Vat.php Normal file
View File

@@ -0,0 +1,136 @@
<?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\Entities;
use AtolOnline\Enums\VatTypes;
use AtolOnline\Helpers;
/**
* Класс, описывающий ставку НДС
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25, 31
*/
class Vat extends Entity
{
/**
* @var string Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106)
*/
private string $type;
/**
* @var float Сумма в рублях, от которой пересчитывается размер НДС
*/
private float $sum;
/**
* Конструктор
*
* @param string $type Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106)
* @param float $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС
*/
public function __construct(string $type, float $rubles)
{
$this->setType($type)->setSum($rubles);
}
/**
* Устанавливает тип ставки НДС
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
*
* @param string $type Тип ставки НДС
* @return $this
*/
public function setType(string $type): self
{
$type = trim($type);
VatTypes::isValid($type) && $this->type = $type;
return $this;
}
/**
* Возвращает тип ставки НДС
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Возвращает исходную сумму, от которой расчитывается размер налога
*
* @return float
*/
public function getSum(): float
{
return $this->sum;
}
/**
* Устанавливает исходную сумму, от которой будет расчитываться итоговый размер НДС.
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
*
* @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС
* @return $this
*/
public function setSum(float $rubles): self
{
$this->sum = $rubles;
return $this;
}
/**
* Возвращает sdрасчитанный итоговый размер ставки НДС в рублях
*
* @return float
* @see https://nalog-nalog.ru/nds/nalogovaya_baza_nds/kak-schitat-nds-pravilno-vychislyaem-20-ot-summy-primer-algoritm/
* @see https://glavkniga.ru/situations/k500734
* @see https://www.b-kontur.ru/nds-kalkuljator-online
*/
public function getCalculated(): float
{
$kopeks = Helpers::toKop($this->sum);
return Helpers::toRub(match ($this->getType()) {
VatTypes::VAT10 => $kopeks * 10 / 100,
VatTypes::VAT18 => $kopeks * 18 / 100,
VatTypes::VAT20 => $kopeks * 20 / 100,
VatTypes::VAT110 => $kopeks * 10 / 110,
VatTypes::VAT118 => $kopeks * 18 / 118,
VatTypes::VAT120 => $kopeks * 20 / 120,
default => 0,
});
}
/**
* Прибавляет указанную сумму к исходной
*
* @param float $rubles
* @return $this
*/
public function addSum(float $rubles): self
{
$this->sum += $rubles;
return $this;
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
return [
'type' => $this->getType(),
'sum' => $this->getCalculated(),
];
}
}

63
src/Enums/AgentTypes.php Normal file
View File

@@ -0,0 +1,63 @@
<?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;
/**
* Константы, определяющие типы агента
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 18
*/
final class AgentTypes extends Enum
{
/**
* Банковский платёжный агент
*/
const BANK_PAYING_AGENT = 'bank_paying_agent';
/**
* Банковский платёжный субагент
*/
const BANK_PAYING_SUBAGENT = 'bank_paying_subagent';
/**
* Платёжный агент
*/
const PAYING_AGENT = 'paying_agent';
/**
* Платёжный субагент
*/
const PAYING_SUBAGENT = 'paying_subagent';
/**
* Поверенный
*/
const ATTRONEY = 'attorney';
/**
* Комиссионер
*/
const COMMISSION_AGENT = 'commission_agent';
/**
* Другой тип агента
*/
const ANOTHER = 'another';
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [1057];
}
}

View File

@@ -1,28 +1,38 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
/**
* Константы, определяющие типы документов коррекции
*
* @package AtolOnline\Constants
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
*/
class CorrectionTypes
final class CorrectionTypes extends Enum
{
/**
* Самостоятельно
*/
const SELF = 'self';
/**
* По предписанию
*/
const INSTRUCTION = 'instruction';
}
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [1173];
}
}

View File

@@ -0,0 +1,36 @@
<?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 [];
}
}

37
src/Enums/Enum.php Normal file
View File

@@ -0,0 +1,37 @@
<?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;
use AtolOnline\Exceptions\InvalidEnumValueException;
/**
* Расширение класса перечисления
*/
abstract class Enum extends \MyCLabs\Enum\Enum
{
/**
* @inheritDoc
* @throws InvalidEnumValueException
*/
public static function isValid($value)
{
return parent::isValid($value)
?: throw new InvalidEnumValueException(static::class, $value);
}
/**
* Возвращает массив тегов ФФД
*
* @return int[]
*/
abstract public static function getFfdTags(): array;
}

View File

@@ -1,54 +1,63 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
/**
* Константы, определяющие признаки способов расчёта. Тег ФФД - 1214.
* Константы, определяющие признаки способов расчёта
*
* @package AtolOnline\Constants
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 22
*/
class PaymentMethods
final class PaymentMethods extends Enum
{
/**
* Предоплата 100% до передачи предмета расчёта
*/
const FULL_PREPAYMENT = 'full_prepayment';
/**
* Частичная предоплата до передачи предмета расчёта
*/
const PREPAYMENT = 'prepayment';
/**
* Аванс
*/
const ADVANCE = 'advance';
/**
* Полная оплата с учётом аванса/предоплаты в момент передачи предмета расчёта
*/
const FULL_PAYMENT = 'full_payment';
/**
* Частичный расчёт в момент передачи предмета расчёта (дальнейшая оплата в кредит)
*/
const PARTIAL_PAYMENT = 'partial_payment';
/**
* Передача предмета расчёта в кредит
*/
const CREDIT = 'credit';
/**
* Оплата кредита
*/
const CREDIT_PAYMENT = 'credit_payment';
}
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [1214];
}
}

View File

@@ -1,108 +1,165 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
/**
* Константы, определяющие признаки предметов расчёта. Тег ФФД - 1212.
* Константы, определяющие признаки предметов расчёта
*
* @package AtolOnline\Constants
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 23
*/
class PaymentObjects
final class PaymentObjects extends Enum
{
/**
* Товар, кроме подакцизного
*/
const COMMODITY = 'commodity';
/**
* Товар подакцизный
*/
const EXCISE = 'excise';
/**
* Работа
*/
const JOB = 'job';
/**
* Услуга
*/
const SERVICE = 'service';
/**
* Ставка азартной игры
*/
const GAMBLING_BET = 'gambling_bet';
/**
* Выигрыш азартной игры
*/
const GAMBLING_PRIZE = 'gambling_prize';
/**
* Лотерея
*/
const LOTTERY = 'lottery';
/**
* Выигрыш лотереи
*/
const LOTTERY_PRIZE = 'lottery_prize';
/**
* Предоставление результатов интеллектуальной деятельности
*/
const INTELLECTUAL_ACTIVITY = 'intellectual_activity';
/**
* Платёж (задаток, кредит, аванс, предоплата, пеня, штраф, бонус и пр.)
*/
const PAYMENT = 'payment';
/**
* Агентское вознаграждение
*/
const AGENT_COMMISSION = 'agent_commission';
/**
* Составной предмет расчёта
* @deprecated Более не используется согласно ФФД 1.05
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25 (payment_object)
*/
const COMPOSITE = 'composite';
/**
* Другой предмет расчёта
*/
const ANOTHER = 'another';
/**
* Имущественное право
*/
const PROPERTY_RIGHT = 'property_right';
/**
* Внереализационный доход
*/
const NON_OPERATING_GAIN = 'non-operating_gain';
/**
* Страховые взносы
*/
const INSURANCE_PREMIUM = 'insurance_premium';
/**
* Торговый сбор
*/
const SALES_TAX = 'sales_tax';
/**
* Курортный сбор
*/
const RESORT_FEE = 'resort_fee';
/**
* Взнос в счёт оплаты пени, штрафе, вознаграждении, бонусе и ином аналогичном предмете расчёта
*/
const AWARD = 'award';
/**
* Залог
*/
const DEPOSIT = 'deposit';
/**
* Расход, уменьшающий доход (в соответствии со статьей 346.16 НК РФ)
*/
const EXPENSE = 'expense';
/**
* Взнос на ОПС ИП
*/
const PEN_INSURANCE_IP = 'pension_insurance_ip';
/**
* Взнос на ОПС
*/
const PEN_INSURANCE = 'pension_insurance';
/**
* Взнос на ОМС ИП
*/
const MED_INSURANCE_IP = 'medical_insurance_ip';
/**
* Взнос на ОМС
*/
const MED_INSURANCE = 'medical_insurance';
/**
* Взнос на ОСС
*/
const SOC_INSURANCE = 'social_insurance';
/**
* Платёж казино
*/
const CASINO_PAYMENT = 'casino_payment';
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [1212];
}
}

View File

@@ -0,0 +1,83 @@
<?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;
/**
* Константы, определяющие виды оплат
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
*/
final class PaymentTypes extends Enum
{
/**
* Расчёт наличными
*/
const CASH = 0;
/**
* Расчёт безналичными
*/
const ELECTRON = 1;
/**
* Предварительная оплата (зачёт аванса)
*/
const PRE_PAID = 2;
/**
* Предварительная оплата (кредит)
*/
const CREDIT = 3;
/**
* Иная форма оплаты (встречное предоставление)
*/
const OTHER = 4;
/**
* Расширенный типы оплаты (5)
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
*/
const ADD_5 = 5;
/**
* Расширенный типы оплаты (6)
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
*/
const ADD_6 = 6;
/**
* Расширенный типы оплаты (7)
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
*/
const ADD_7 = 7;
/**
* Расширенный типы оплаты (8)
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
*/
const ADD_8 = 8;
/**
* Расширенный типы оплаты (9)
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
*/
const ADD_9 = 9;
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [1031, 1081, 1215, 1216, 1217];
}
}

View File

@@ -1,48 +1,50 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
use MyCLabs\Enum\Enum;
/**
* Константы, определяющие типы операций (чеков)
*
* @package AtolOnline\Constants
*/
class ReceiptOperationTypes
final class ReceiptOperationTypes extends Enum
{
/**
* Приход (мы продали)
*/
const SELL = 'sell';
/**
* Возврат прихода (нам вернули предмет расчёта, мы вернули деньги)
* Возврат прихода (нам вернули предмет расчёта, мы вернули средства)
*/
const SELL_REFUND = 'sell_refund';
/**
* Коррекция прихода
*/
const SELL_CORRECTION = 'sell_correction';
/**
* Расход (мы купили)
*/
const BUY = 'buy';
/**
* Возврат расхода (мы вернули предмет расчёта, нам вернули деньги)
* Возврат расхода (мы вернули предмет расчёта, нам вернули средства)
*/
const BUY_REFUND = 'buy_refund';
/**
* Коррекция прихода
* Коррекция прихода (догоняем неучтённые средства)
*/
const BUY_CORRECTION = 'buy_correction';
}

View File

@@ -1,48 +1,58 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
/**
* Константы, определяющие типы налогообложения
*
* @package AtolOnline\Constants
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
*/
class SnoTypes
final class SnoTypes extends Enum
{
/**
* Общая СН
*/
const OSN = 'osn';
/**
* Упрощенная СН (доходы)
*/
const USN_INCOME = 'usn_income';
/**
* Упрощенная СН (доходы минус расходы)
*/
const USN_INCOME_OUTCOME = 'usn_income_outcome';
/**
* Единый налог на вмененный доход
*/
const ENDV = 'envd';
/**
* Единый сельскохозяйственный налог
*/
const ESN = 'esn';
/**
* Патентная СН
*/
const PATENT = 'patent';
}
/**
* @inheritDoc
*/
public static function getFfdTags(): array
{
return [1055];
}
}

View File

@@ -1,56 +1,60 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
use MyCLabs\Enum\Enum;
/**
* Константы, определяющие типы ставок НДС
*
* @package AtolOnline\Constants
* Теги ФФД: 1199, 1105, 1104, 1103, 1102, 1107, 1106
*/
class VatTypes
final class VatTypes extends Enum
{
/**
* Без НДС
*/
const NONE = 'none';
/**
* НДС 0%
*/
const VAT0 = 'vat0';
/**
* НДС 10%
*/
const VAT10 = 'vat10';
/**
* НДС 18%
*/
const VAT18 = 'vat18';
/**
* НДС 20%
*/
const VAT20 = 'vat20';
/**
* НДС 10/110%
*/
const VAT110 = 'vat110';
/**
* НДС 18/118%
*/
const VAT118 = 'vat118';
/**
* НДС 20/120%
*/

View File

@@ -0,0 +1,38 @@
<?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;
use Exception;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
*/
class AtolException extends Exception
{
/**
* @var int[] Теги ФФД
*/
protected array $ffd_tags = [];
/**
* Конструктор
*
* @param string $message Сообщение
* @param int[] $ffd_tags Переопредление тегов ФФД
*/
public function __construct(string $message = '', array $ffd_tags = [])
{
parent::__construct(
($message ?: $this->message) . ' [Теги ФФД: ' . implode(', ', $ffd_tags ?: $this->ffd_tags) . ']'
);
}
}

View File

@@ -0,0 +1,32 @@
<?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;
use AtolOnline\Api\KktResponse;
use Exception;
/**
* Исключение, возникающее при неудачной авторизации
*/
class AuthFailedException extends Exception
{
/**
* Конструктор
*
* @param KktResponse $response
* @param string $message
*/
public function __construct(KktResponse $response, string $message = '')
{
parent::__construct(($message ?: 'Ошибка авторизации: ') . ': ' . $response);
}
}

View File

@@ -0,0 +1,22 @@
<?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;
use AtolOnline\Entities\CorrectionInfo;
/**
* Исключение, возникающее при попытке зарегистрировать документ коррекции без соотв. данных
*/
class EmptyCorrectionInfoException extends AtolException
{
protected $message = 'Документ должен содержать объект ' . CorrectionInfo::class;
}

View File

@@ -0,0 +1,28 @@
<?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;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать пустой email
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
*/
class EmptyEmailException extends AtolException
{
protected $message = 'Email не может быть пустым';
protected array $ffd_tags = [
Ffd105Tags::CLIENT_PHONE_EMAIL,
Ffd105Tags::COMPANY_EMAIL,
];
}

View File

@@ -0,0 +1,21 @@
<?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;
/**
* Исключение, возникающее при попытке указать пустой логин
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12
*/
class EmptyLoginException extends AtolException
{
protected $message = 'Логин не может быть пустым';
}

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 EmptyMonitorDataException extends AtolException
{
protected $message = 'Не возможно создать объект ККт без данных от мониторинга';
}

View File

@@ -0,0 +1,21 @@
<?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;
/**
* Исключение, возникающее при попытке указать пустой пароль
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12
*/
class EmptyPasswordException extends AtolException
{
protected $message = 'Пароль не может быть пустым';
}

View File

@@ -1,23 +1,20 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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;
/**
* Исключение, возникающее при попытке указать невалидный callback_url
*
* @package AtolOnline\Exceptions
*/
class AtolInvalidCallbackUrlException extends AtolException
class InvalidCallbackUrlException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Invalid callback URL';
}
protected $message = 'Невалидный callback_url';
}

View File

@@ -0,0 +1,37 @@
<?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;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при ошибке валидации email
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
*/
class InvalidEmailException extends AtolException
{
protected array $ffd_tags = [
Ffd105Tags::CLIENT_PHONE_EMAIL,
Ffd105Tags::COMPANY_EMAIL,
];
/**
* Конструктор
*
* @param string $email
*/
public function __construct(string $email = '')
{
parent::__construct("Невалидный email: '$email'");
}
}

View File

@@ -0,0 +1,38 @@
<?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;
use AtolOnline\Enums\Enum;
/**
* Исключение, возникающее при ошибке валидации типа агента
*/
class InvalidEnumValueException extends AtolException
{
/**
* Конструктор
*
* @param string $enum
* @param mixed $value
* @param string $message
* @param array $ffd_tags
*/
public function __construct(string $enum, mixed $value, string $message = '', array $ffd_tags = [])
{
/** @var $enum Enum */
parent::__construct(
($message ?: "Некорректное значение $enum::$value.") .
" Допустимые значения: " . implode(', ', $enum::toArray()),
$ffd_tags ?: $enum::getFfdTags()
);
}
}

View File

@@ -0,0 +1,40 @@
<?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;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать ИНН некорректной длины
*/
class InvalidInnLengthException extends AtolException
{
protected array $ffd_tags = [
Ffd105Tags::MTO_INN,
Ffd105Tags::COMPANY_INN,
Ffd105Tags::SUPPLIER_INN,
Ffd105Tags::CLIENT_INN,
];
/**
* Конструктор
*
* @param string $inn
* @param string $message
*/
public function __construct(string $inn = '', string $message = '')
{
parent::__construct(
$message ?: 'Длина ИНН должна быть 10 или 12 цифр, фактически - ' . strlen($inn),
);
}
}

View File

@@ -0,0 +1,26 @@
<?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;
/**
* Исключение, возникающее при работе с невалидным JSON
*/
class InvalidJsonException extends AtolException
{
/**
* Конструктор
*/
public function __construct()
{
parent::__construct('[' . json_last_error() . '] ' . json_last_error_msg());
}
}

View File

@@ -0,0 +1,35 @@
<?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;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать некорректный адрес места расчётов
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
*/
class InvalidPaymentAddressException extends AtolException
{
protected array $ffd_tags = [Ffd105Tags::COMPANY_PADDRESS];
/**
* Конструктор
*
* @param string $address
* @param string $message
*/
public function __construct($address = '', $message = "")
{
parent::__construct($message ?: "Некорректный адрес места расчётов: '$address'");
}
}

View File

@@ -0,0 +1,38 @@
<?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;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при ошибке валидации номера телефона
*/
class InvalidPhoneException extends AtolException
{
protected array $ffd_tags = [
Ffd105Tags::CLIENT_PHONE_EMAIL,
Ffd105Tags::PAGENT_PHONE,
Ffd105Tags::RPO_PHONES,
Ffd105Tags::MTO_PHONES,
Ffd105Tags::SUPPLIER_PHONES,
];
/**
* Конструктор
*
* @param string $phone
*/
public function __construct(string $phone = '')
{
parent::__construct("Невалидный номер телефона: '$phone'");
}
}

View File

@@ -0,0 +1,31 @@
<?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;
/**
* Исключение, возникающее при ошибке валидации UUID
*/
class InvalidUuidException extends AtolException
{
/**
* Конструктор
*
* @param $uuid
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(?string $uuid = null)
{
parent::__construct('Невалидный UUID' . ($uuid ? ': ' . $uuid : ''));
}
}

View File

@@ -0,0 +1,34 @@
<?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 NotEnoughMonitorDataException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Невозможно создать объект ККТ без следующих атрибутов: ';
/**
* Конструктор
*
* @param array $props_diff
* @param string $message
*/
public function __construct(array $props_diff, string $message = '')
{
parent::__construct($message ?: $this->message . implode(', ', $props_diff));
}
}

View File

@@ -0,0 +1,25 @@
<?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;
use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать слишком высокую цену (сумму)
*/
class TooHighPriceException extends TooManyException
{
protected $message = 'Слишком высокая цена';
protected array $ffd_tags = [Ffd105Tags::ITEM_PRICE];
protected float $max = Constraints::MAX_COUNT_ITEM_PRICE;
}

View File

@@ -1,23 +1,23 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
/*
* 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;
use AtolOnline\Constants\Constraints;
/**
* Исключение, возникающее при попытке указать слишком длинный callback_url
*
* @package AtolOnline\Exceptions
*/
class AtolCallbackUrlTooLongException extends AtolTooLongException
class TooLongCallbackUrlException extends TooLongException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Callback URL is too long';
}
protected $message = 'Слишком длинный адрес колбека';
protected float $max = Constraints::MAX_LENGTH_CALLBACK_URL;
}

View File

@@ -0,0 +1,25 @@
<?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;
use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать слишком длинное имя кассира
*/
class TooLongCashierException extends TooLongException
{
protected $message = 'Слишком длинное имя кассира';
protected float $max = Constraints::MAX_LENGTH_CASHIER_NAME;
protected array $ffd_tags = [Ffd105Tags::CASHIER];
}

View File

@@ -0,0 +1,25 @@
<?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;
use AtolOnline\Constants\Constraints;
use AtolOnline\Constants\Ffd105Tags;
/**
* Исключение, возникающее при попытке указать слишком длинный телефон или email покупателя
*/
class TooLongClientContactException extends TooLongException
{
protected $message = 'Cлишком длинный телефон или email покупателя';
protected float $max = Constraints::MAX_LENGTH_CLIENT_CONTACT;
protected array $ffd_tags = [Ffd105Tags::CLIENT_PHONE_EMAIL];
}

Some files were not shown because too many files have changed in this diff Show More