70 Commits

Author SHA1 Message Date
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
e1fb74ac01 Доудалено упоминание о Schemas 2020-06-07 20:28:14 +08:00
8d9f218280 Мелкофиксы по документации и roadmap 2020-06-07 20:12:50 +08:00
9ed999f9fc Улучшен класс Kkt
+ setCallbackUrl(): валидация
* setLogin() и setPassword() более не зависят от тестового режима
* setPassword(): выброс AtolKktPasswordTooLongException
* auth(): выброс AtolAuthFailedException
* valid_strlen()
* Constraints::MAX_LENGTH_LOGIN
* Constraints::MAX_LENGTH_PASSWORD
* Constraints::PATTERN_CALLBACK_URL
* phpdoc
* исправление опечаток при выбросе некоторых исключений
2020-06-07 20:12:50 +08:00
989c06a383 Улучшен класс Item
* valid_strlen()
* Constraints::MAX_LENGTH_ITEM_NAME
* Constraints::MAX_LENGTH_MEASUREMENT_UNIT
* Constraints::MAX_LENGTH_USER_DATA
2020-06-07 20:12:23 +08:00
9bd99c81a9 Улучшен класс Document
* валидация в setCashier() только если передан не-null
* valid_strlen()
* Constraints::MAX_LENGTH_CASHIER_NAME
* phpdoc
2020-06-07 20:12:23 +08:00
2943d93962 Улучшен класс Company
* valid_strlen()
* Constraints::MAX_LENGTH_PAYMENT_ADDRESS
* phpdoc
2020-06-07 20:12:23 +08:00
8eb309bc58 Улучшен класс Сlient
* valid_strlen()
* Constraints::MAX_LENGTH_CLIENT_NAME
* Constraints::MAX_LENGTH_CLIENT_PHONE
* phpdoc
2020-06-07 20:12:23 +08:00
b74f652127 Улучшен trait HasInn
* Constraints::PATTERN_INN
* phpdoc
2020-06-07 20:12:23 +08:00
1061914a5f Улучшен trait HasEmail
* valid_strlen()
* Constraints::MAX_LENGTH_EMAIL
* phpdoc
2020-06-07 20:12:23 +08:00
5424726a97 Мелкофиксы в исключениях
* использование valid_strlen() в AtolInnWrongLengthException и AtolTooLongException
* фикс опечатки в AtolEmailTooLongException
2020-06-07 20:12:23 +08:00
2b3713db69 Новые исключения
+ AtolAuthFailedException
+ AtolCallbackUrlTooLongException
+ AtolInvalidCallbackUrlException
+ AtolKktPasswordTooLongException
2020-06-07 20:12:23 +08:00
a6b57115b6 Новый класс Constraints 2020-06-07 20:12:23 +08:00
12e0e49c9b Удалёно всё, что связано со схемами 2020-06-07 20:12:22 +08:00
3ffab562f8 Новый файл helpers 2020-06-07 20:12:22 +08:00
c05e013a5a Освежил документацию в части работы с объектом Kkt 2020-06-04 22:29:36 +08:00
e282de7e08 Вернул назад переопределение параметров компании при тестовом режиме: ИНН, адрес места расчётов
Также появился новый класс с константами тестовых параметров
2020-06-04 22:12:34 +08:00
c14b680be4 Проброс external_id на регистрацию документа 2020-06-01 04:12:23 +08:00
7558cb6638 Убрал схему Атола валидации количества НДС, оплат и предметов 2020-06-01 03:58:31 +08:00
d3fe2cba9a Удалена перезапись компании в тестовом режиме. Новый метод Kkt::setAuthToken() 2020-06-01 03:26:25 +08:00
e70a65fa44 Улучшен метод Kkt::registerDocument
+ теперь принимает любой собственного external_id, если пусто - сгенерирует uuid
* исправлен недостаток полей компании в тестовом режиме
* уточнения phpdoc
2020-05-30 04:29:44 +08:00
ba5166f2cf Метод Kkt::getAuthToken() теперь открыт 2020-05-30 03:45:06 +08:00
93f5186b15 Перезапись компании в документе при тестовом режиме 2020-05-30 01:59:24 +08:00
f2b4952aa5 Установка адреса callback для тестового режима 2020-05-30 01:59:24 +08:00
7899daf421 Снова фикс ошибок при приведении документа к json-строке 2020-05-30 01:59:23 +08:00
4cf6e81d5f Исправлены тесты компании и клиента 2020-05-29 22:24:15 +08:00
9d7dd75cd9 Испарвлен тест на длину имени предмета расчёта 2020-05-29 22:15:47 +08:00
4d7e5dd76e Фикс возвращаемого значения KktResponse::getContent() 2020-05-29 22:06:04 +08:00
0f658d38a9 Фикс ошибок при приведении докумнета к json-строке 2020-05-29 22:05:35 +08:00
ca32fe5923 Повсюду улучшена проверка длин строк 2020-05-28 22:56:26 +08:00
9ea1c81666 Скорректирован Document::jsonSerialize() - не выдаёт пустого кассира 2020-05-28 22:55:53 +08:00
12b98dcdac Document::fromRaw() теперь парсит входящий json как массив, а не объект 2020-05-28 22:55:27 +08:00
1fec446ce0 Фикс тестов НДС 2020-05-28 01:31:37 +08:00
6256c14522 Обновления в composer.lock 2020-05-28 01:25:27 +08:00
7dff642f0a Бейджик в readme + фикс composer.json 2020-05-28 01:14:53 +08:00
670f440620 Github actions только на мастере 2020-05-28 01:13:08 +08:00
3385420005 Merge branch 'dev'. Мнда, я забыл слить изменения... 2020-05-28 00:56:31 +08:00
ff42eebb8c [RELEASE] v0.2.1-b 2020-05-28 00:53:18 +08:00
9121d44a26 Merge branch 'master' into dev 2020-05-28 00:43:28 +08:00
600a505c83 Генерация объекта документа по сырой json-строке 2020-05-28 00:41:58 +08:00
4fccf7809d Небольшие правки по Item (улучшена проверка длины ед. изм.) 2020-05-28 00:41:01 +08:00
2d29ff3994 Фикс исключения некорректного UUID в методе Kkt::getDocumentStatus() 2020-05-28 00:39:42 +08:00
954843e3ad Фикс исключения превышения количества ставок НДС в массиве 2020-05-28 00:38:48 +08:00
a619b06a48 Исправлен расчёт НДС для документа 2020-05-28 00:38:16 +08:00
0bb194b1f8 Улучшены исключения
- добавлены теги ФФД для удобства
- тексты исключений переведены на англ. язык во избежание проблем с кодировками
- новые исключения TooMany и TooLong для удобства, подходящие по смыслу исключения унаследованы от них
2020-05-27 23:10:30 +08:00
115 changed files with 6930 additions and 3434 deletions

2
.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']
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,23 +0,0 @@
name: Composer and phpunit tests
on: [push]
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,5 +1,18 @@
# АТОЛ Онлайн
---
**В этой ветке проводится глубокий рефакторинг и активная подготовка к `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/).
Текущая поддерживаемая версия API: **5.1**
@@ -8,7 +21,7 @@
## Системные требования
* PHP 7.2+
* PHP 7.4+
* composer
* php-json
@@ -101,8 +114,6 @@
## Дополнительные ресурсы
**Discord-сервер для обсуждения этой библиотеки: [discord.gg/mFYTQmp](https://discord.gg/mFYTQmp)**
Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md)
Официальные ресурсы АТОЛ Онлайн:

View File

@@ -25,9 +25,8 @@
- [ ] Тесты для регистрации документа расхода
- [ ] Тесты для регистрации документа возврата расхода
- [ ] Тесты для регистрации документа коррекции расхода
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках. Рубли (дроби) должны быть только в JSON-представлениях
- [ ] Валидатор схемы для документов прихода, возврата прихода, расхода, возврата расхода
- [ ] Валидатор схемы для документов коррекции прихода, коррекции расхода
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках.
Рубли (дроби) должны быть только в JSON-представлениях
## Поддержка методов API (регистрация документов)

View File

@@ -3,13 +3,23 @@
"description": "Library to use cloud cash register in e-commerce according to Russian Federal Law #54",
"license": "MIT",
"type": "library",
"version": "0.1.1-b",
"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",
@@ -20,27 +30,39 @@
"support": {
"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": "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": {
"classmap": [
"src/AtolOnline/Api/",
"src/AtolOnline/Api/Schemas/",
"src/AtolOnline/Exceptions/",
"src/AtolOnline/Entities/",
"src/AtolOnline/Traits/",
"src/AtolOnline/Constants/",
"tests/"
]
"psr-4": {
"AtolOnline\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"AtolOnlineTests\\": "tests/"
}
},
"scripts": {
"test": "vendor/bin/phpunit --colors=always",
"test-cov": "vendor/bin/phpunit --coverage-html coverage"
}
}

2363
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -46,10 +46,10 @@ $customer = (new AtolOnline\Entities\Client())
* `AtolEmailValidateException` (если email невалиден).
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина строка ИНН некорректна).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
Метод `setName()` проверяет входную строку на длину (до 256 символов).
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное наименование).
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное имя).
Метод `setPhone()` чистит входную строку от всех символов, кроме цифр и знака `+`, и проверяет длину (до 64 символов).
Выбрасывает исключение `AtolPhoneTooLongException` (если слишком длинный номер телефона).

View File

@@ -46,7 +46,7 @@ $company = (new AtolOnline\Entities\Company())
* `AtolEmailValidateException` (если email невалиден).
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина строка ИНН некорректна).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
Метод `setPaymentAddress()` проверяет длину (до 256 символов).
Выбрасывает исключение `AtolPaymentAddressTooLongException` (если слишком длинный адрес места расчётов).

View File

@@ -164,4 +164,4 @@ $json_array = $doc->jsonSerialize();
---
[Вернуться к содержанию](readme.md)
[Вернуться к содержанию](readme.md)

View File

@@ -49,12 +49,18 @@ $kkt->getGroup();
Эти параметры нужно задать [объекту компании](/docs/company.md), который будет передаваться в документах через эту ККТ.
<a name='testmode'></a>
## Тестовый режим
На самом деле, в АТОЛ Онлайн нет понятия *тестовая операция* или чего-то в этом духе.
АТОЛ предоставляет нам отдельную тестовую ККТ.
АТОЛ предоставляет нам отдельную тестовую среду (ККТ).
[Её настройки](https://online.atol.ru/files/ffd/test_sreda.txt) уже указаны в коде библиотеки.
*Под тестовым режимом работы подразумевается использование этой тестовой ККТ.*
*Под тестовым режимом работы подразумевается использование другой (тестовой) ККТ.*
При включенном тестовом режиме:
* меняется логин, пароль и группа (для обращения на тестовую ККТ)
* между авторизацией и операцией над документом, в `Company` документа переопределяется ИНН, СНО и адрес места
расчётов на те, что указаны в [параметрах тестовой среды](https://online.atol.ru/files/ffd/test_sreda.txt).
В библиотеке есть переключатель настроек ККТ.
С его помощью можете поменять вашу боевую ККТ на тестовую и наоборот.
@@ -70,13 +76,43 @@ $kkt->setTestMode(false); // выключить
> Если вы включили тестовый режим (как показано выше), то используются именно эта ККТ, а не ваша.
> После выключения тестового режима настройки доступа к ККТ меняются на ваши (используется уже ваша ККТ).
Если по каким-то причинам у вас не получится использовать тестовый режим, вы можете проводить свои тесты в боевом режиме (на собственной ККТ).
Для включения тестового режима необязательно задавать параметры боевой ККТ.
Если по каким-то причинам у вас не получится использовать тестовый режим, вы можете проводить свои тесты в боевом
режиме (на собственной ККТ).
В этом случае важно понимать следующее:
1. сразу после оформления документа **прихода** необходимо оформлять точно такой же документ **возврата прихода**;
2. [вы обязательно забудете о пункте 1](http://murphy-law.net.ru/basics.html);
3. пп. 1 и 2 в любом случае скажутся на ваших финансовых отчётах;
4. вся ответственность за пп. 1-3 и последствия ложится только на вас.
## Авторизация на ККТ
Перед первым запросом на ККТ происходит аутентификация на сервере по логину и паролю.
В ответ приходит авторизационный токен, срок жизни коего равен **24 часам**.
После первой успешной операции возможно получить этот токен следующим образом:
```php
$kkt->getAuthToken(); // вернёт строку длиной 128 символа
```
Этот токен можно сохранить и переиспользовать в течение всего срока его жизни.
Спустя это время следует получить новый токен.
Для дальнейшего использования однажды полученный токен следует указывать следующим образом:
```php
$kkt->setAuthToken($token_string);
```
Если токен был установлен перед выполнением операции, то при выполнении операции будет использоваться именно он, а новый
запрашиваться не будет. Если операция завершится ошибочно из-за истёкшего токена, следует повторить операцию без
использования метода `setAuthToken()`, либо обнулив его следующим образом:
```php
$kkt->setAuthToken(null);
```
## Регистрация документа
Для регистрации документа **прихода** необходимо вызвать метод `sell()`:
@@ -106,29 +142,64 @@ $result = $kkt->buyRefund($document);
Для операций, перечисленных выше, документы не должны содержать [данных коррекции](/docs/documents.md#correction).
Тогда как для операций коррекции, которые описаны ниже, эти данные должны присутствовать.
Для регистрации документа **коррекции прихода** необходимо вызвать метод `sellRefund()`:
Для регистрации документа **коррекции прихода** необходимо вызвать метод `sellCorrection()`:
```php
$result = $kkt->sellCorrection($document);
```
Для регистрации документа **коррекции расхода** необходимо вызвать метод `buyRefund()`:
Для регистрации документа **коррекции расхода** необходимо вызвать метод `buyCorrection()`:
```php
$result = $kkt->buyCorrection($document);
```
Любой из перечисленных выше шести методов может выбросить исключение `AtolAuthFailedException` при ошибке
аутентификации или авторизации.
### Собственный идентификатор документа
Каждый документ, переданный на ККТ для регистрации, всегда имеет свой идентификатор, абсолютно уникальный среди всех
документов когда-либо регистрировавшихся на ККТ, даже если при регистрации были ошибки.
По умолчанию это UUID версии 4.
Чтобы использовать собственный идентификатор, следует передать нужное строковое значение вторым параметром в любой из
шести описанных выше методов, например:
```php
$kkt->sell($document, $my_unique_id);
$kkt->sellRefund($document, $my_unique_id);
$kkt->buy($document, $my_unique_id);
$kkt->buyRefund($document, $my_unique_id);
$kkt->sellCorrection($document, $my_unique_id);
$kkt->buyCorrection($document, $my_unique_id);
```
Если `$my_unique_id` равен `null` или пустой строке, то будет сгенерирован новый UUID.
Узнать его можно будет только в ответе от ККТ.
### Передача callback_url
Перед регистрацией документа можно указать `callback_url`.
АТОЛ отправит на указанный URL результат регистрации.
Вам необходимо расположить по этому адресу скрипт, обрабатывающий этот результат.
По этому адресу должен располагаться код, который будет обрабатывать этот результат.
```php
$kkt->setCallbackUrl('http://example.com/process-kkt-result');
$kkt->getCallbackUrl();
```
Метод `setCallbackUrl()` проверяет входную строку на длину (до 256 символов) и валидность формата url по
регулярному выражению:
```
^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%$#_]*)?$
```
Выбрасывает исключения:
* `AtolCallbackUrlTooLongException` (если слишком длинный url);
* `AtolInvalidCallbackUrlException` (если url невалиден).
## Обработка результата регистрации
Методы `sell()`, `sellRefund()`, `sellCorrection()`, `buy()`, `buyRefund()` и `buyCorrection()` возвращают объект `AtolOnline\Api\KktResponse`.
@@ -138,7 +209,7 @@ $kkt->getCallbackUrl();
Этот объект содержит в себе HTTP-код ответа, массив заголовков и JSON-декодированные данные тела ответа.
```php
$result = $kkt->getLastResponse();
$result = $kkt->getLastResponse(); // вернёт последний ответ от API
$headers = $result->getHeaders(); // вернёт заголовки
$code = $result->getCode(); // вернёт код ответа
$body = $result->getContent(); // вернёт JSON-декодированное тело ответа
@@ -158,7 +229,7 @@ $err_text = $result->error->text;
Проверка корректности ответа (отсутствия ошибок) работает через метод `isValid()`:
```php
$kkt->isValid(); // вернёт true, если ошибок нет
$kkt->getLastResponse()->isValid(); // вернёт true, если ошибок нет
```
## Проверка статуса документа

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, Constraints::MAX_LENGTH_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, Constraints::MAX_LENGTH_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 = [];
$limit && $params['limit'] = $limit;
$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
* @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,503 +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\{Entities\Document,
Exceptions\AtolCorrectionInfoException,
Exceptions\AtolKktLoginEmptyException,
Exceptions\AtolKktLoginTooLongException,
Exceptions\AtolKktPasswordEmptyException,
Exceptions\AtolUuidValidateException,
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 Пароль ККТ не может быть пустым
* @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 (!$this->isTestMode()) {
if (empty($login)) {
throw new AtolKktLoginEmptyException();
} elseif (strlen($login) > 100) {
throw new AtolKktLoginTooLongException($login, 100);
}
}
$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 Пароль ККТ не может быть пустым
*/
public function setPassword(string $password)
{
if (!$this->isTestMode()) {
if (empty($password)) {
throw new AtolKktPasswordEmptyException();
}
}
$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
*/
public function setCallbackUrl(string $url)
{
$this->kkt_config['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
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
*/
public function sell(Document $document)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('sell', 'receipt', $document);
}
/**
* Регистрирует документ возврата прихода
*
* @param \AtolOnline\Entities\Document $document
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
*/
public function sellRefund(Document $document)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats());
}
/**
* Регистрирует документ коррекции прихода
*
* @param \AtolOnline\Entities\Document $document
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
*/
public function sellCorrection(Document $document)
{
if (!$document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('sell_correction', 'correction', $document);
}
/**
* Регистрирует документ расхода
*
* @param \AtolOnline\Entities\Document $document
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
*/
public function buy(Document $document)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('buy', 'receipt', $document);
}
/**
* Регистрирует документ возврата расхода
*
* @param \AtolOnline\Entities\Document $document
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
*/
public function buyRefund(Document $document)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats());
}
/**
* Регистрирует документ коррекции расхода
*
* @param Document $document
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
*/
public function buyCorrection(Document $document)
{
if (!$document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('buy_correction', 'correction', $document);
}
/**
* Проверяет статус чека на ККТ один раз
*
* @param string $uuid UUID регистрации
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolUuidValidateException Некорректный UUID документа
*/
public function getDocumentStatus(string $uuid)
{
$uuid = trim($uuid);
if (!Uuid::isValid($uuid)) {
throw new AtolUuidValidateException($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\AtolException Некорректный UUID документа
*/
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;
}
/**
* Сбрасывает настройки ККТ по умолчанию
*/
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'] = 'v4-online-atol-ru_4179';
$this->kkt_config['test']['login'] = 'v4-online-atol-ru';
$this->kkt_config['test']['pass'] = 'iGFFuihss';
$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->auth_token;
}
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
* @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
*/
protected function auth()
{
if (!$this->getAuthToken()) {
$result = $this->sendAtolRequest('GET', 'getToken', [
'login' => $this->getLogin(),
'pass' => $this->getPassword(),
]);
if (!$result->isValid() || !$result->getContent()->token) {
return false;
}
$this->auth_token = $result->getContent()->token;
}
return true;
}
/**
* Отправляет документ на регистрацию
*
* @param string $api_method Метод API
* @param string $type Тип документа: receipt, correction
* @param \AtolOnline\Entities\Document $document Объект документа
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \Exception
*/
protected function registerDocument(string $api_method, string $type, Document $document)
{
$type = trim($type);
if (!in_array($type, ['receipt', 'correction'])) {
throw new AtolWrongDocumentTypeException($type);
}
$this->auth();
$data = [
'timestamp' => date('d.m.y H:i:s'),
'external_id' => Uuid::uuid4()->toString(),
'service' => ['callback_url' => $this->getCallbackUrl()],
$type => $document,
];
return $this->sendAtolRequest('POST', trim($api_method), $data);
}
/**
* Возвращает текущий токен авторизации
*
* @return string
*/
protected function getAuthToken()
{
return $this->auth_token;
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace AtolOnline\Api;
abstract class AtolSchema
{
/**
* @return mixed
*/
public static function get()
{
return static::$json
?? static::$json = json_decode(file_get_contents(static::$URL));
}
/**
* @return false|string
*/
public static function json()
{
return json_encode(static::get());
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace AtolOnline\Api;
class CorrectionSchema extends AtolSchema
{
/**
* @var
*/
protected static $json;
/**
* Адрес схемы
*/
protected static $URL = 'https://online.atol.ru/possystem/v4/schema/correction';
}

View File

@@ -1,17 +0,0 @@
<?php
namespace AtolOnline\Api;
class SellSchema extends AtolSchema
{
/**
* @var
*/
protected static $json;
/**
* Адрес схемы
*/
protected static $URL = 'https://online.atol.ru/possystem/v4/schema/sell';
}

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,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\{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
* @throws \AtolOnline\Exceptions\AtolEmailValidateException
* @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 (strlen($name) > 256) {
throw new AtolNameTooLongException($name, 256);
}
$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 (strlen($phone) > 64) {
throw new AtolPhoneTooLongException($phone, 64);
}
$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,137 +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\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
* @throws AtolEmailValidateException
* @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 (strlen($payment_address) > 256) {
throw new AtolPaymentAddressTooLongException($payment_address, 256);
}
$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,354 +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\AtolCashierTooLongException;
/**
* Класс, описывающий документ
*
* @package AtolOnline\Entities
*/
class Document extends Entity
{
/**
* @var \AtolOnline\Entities\ItemArray Массив предметов расчёта
*/
protected $items;
/**
* @var \AtolOnline\Entities\VatArray Массив ставок НДС
*/
protected $vats;
/**
* @var \AtolOnline\Entities\PaymentArray Массив оплат
*/
protected $payments;
/**
* @var \AtolOnline\Entities\Company Объект компании (продавца)
*/
protected $company;
/**
* @var \AtolOnline\Entities\Client Объект клиента (покупателя)
*/
protected $client;
/**
* @var int Итоговая сумма чека. Тег ФФД - 1020.
*/
protected $total = 0;
/**
* @var string ФИО кассира. Тег ФФД - 1021.
*/
protected $cashier;
/**
* @var \AtolOnline\Entities\CorrectionInfo Данные коррекции
*/
protected $correction_info;
/**
* Document constructor.
*
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
*/
public function __construct()
{
$this->vats = new VatArray();
$this->payments = new PaymentArray();
$this->items = new ItemArray();
}
/**
* Удаляет все налоги из документа и предметов расчёта
*
* @return $this
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
*/
public function clearVats()
{
$this->setVats([]);
foreach ($this->getItems() as &$item) {
$item->setVatType(null);
}
$this->calcTotal();
return $this;
}
/**
* Добавляет новую ставку НДС в массив ставок НДС
*
* @param \AtolOnline\Entities\Vat $vat Объект ставки НДС
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
*/
public function addVat(Vat $vat)
{
if (count($this->getVats()) == 0 && !$vat->getSum()) {
$vat->setSum($this->calcTotal());
}
$this->vats->add($vat);
$this->calcTotal();
return $this;
}
/**
* Возвращает массив ставок НДС
*
* @return \AtolOnline\Entities\Vat[]
*/
public function getVats(): array
{
return $this->vats->get();
}
/**
* Устанавливает массив ставок НДС
*
* @param \AtolOnline\Entities\Vat[] $vats Массив ставок НДС
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \Exception
*/
public function setVats(array $vats)
{
$this->vats->set($vats);
$this->calcTotal();
return $this;
}
/**
* Добавляет новую оплату в массив оплат
*
* @param \AtolOnline\Entities\Payment $payment Объект оплаты
* @return $this
* @throws \Exception
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат
*/
public function addPayment(Payment $payment)
{
if (count($this->getPayments()) == 0 && !$payment->getSum()) {
$payment->setSum($this->calcTotal());
}
$this->payments->add($payment);
return $this;
}
/**
* Возвращает массив оплат
*
* @return \AtolOnline\Entities\Payment[]
*/
public function getPayments(): array
{
return $this->payments->get();
}
/**
* Устанавливает массив оплат
*
* @param \AtolOnline\Entities\Payment[] $payments Массив оплат
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат
*/
public function setPayments(array $payments)
{
$this->payments->set($payments);
return $this;
}
/**
* Добавляет новый предмет расчёта в массив предметов расчёта
*
* @param \AtolOnline\Entities\Item $item Объект предмета расчёта
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
*/
public function addItem(Item $item)
{
$this->items->add($item);
return $this;
}
/**
* Возвращает массив предметов расчёта
*
* @return \AtolOnline\Entities\Item[]
*/
public function getItems(): array
{
return $this->items->get();
}
/**
* Устанавливает массив предметов расчёта
*
* @param \AtolOnline\Entities\Item[] $items Массив предметов расчёта
* @return $this
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
*/
public function setItems(array $items)
{
$this->items->set($items);
return $this;
}
/**
* Возвращает заданного клиента (покупателя)
*
* @return Client
*/
public function getClient(): Client
{
return $this->client;
}
/**
* Устанавливает клиента (покупателя)
*
* @param Client|null $client
* @return $this
*/
public function setClient(?Client $client)
{
$this->client = $client;
return $this;
}
/**
* Возвращает заданную компанию (продавца)
*
* @return Company
*/
public function getCompany(): Company
{
return $this->company;
}
/**
* Устанавливает компанию (продавца)
*
* @param Company|null $company
* @return $this
*/
public function setCompany(?Company $company)
{
$this->company = $company;
return $this;
}
/**
* Возвращает ФИО кассира. Тег ФФД - 1021.
*
* @return string|null
*/
public function getCashier(): ?string
{
return $this->cashier;
}
/**
* Устанавливает ФИО кассира. Тег ФФД - 1021.
*
* @param string|null $cashier
* @return $this
* @throws \AtolOnline\Exceptions\AtolCashierTooLongException
*/
public function setCashier(?string $cashier)
{
$cashier = trim($cashier);
if (strlen($cashier) > 64) {
throw new AtolCashierTooLongException($cashier);
}
$this->cashier = $cashier;
return $this;
}
/**
* Возвращает данные коррекции
*
* @return \AtolOnline\Entities\CorrectionInfo|null
*/
public function getCorrectionInfo(): ?CorrectionInfo
{
return $this->correction_info;
}
/**
* Устанавливает данные коррекции
*
* @param \AtolOnline\Entities\CorrectionInfo|null $correction_info
* @return $this
*/
public function setCorrectionInfo(?CorrectionInfo $correction_info)
{
$this->correction_info = $correction_info;
return $this;
}
/**
* Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020.
*
* @return float
* @throws \Exception
*/
public function calcTotal()
{
$sum = 0;
foreach ($this->items->get() as $item) {
$sum += $item->calcSum();
}
foreach ($this->vats->get() as $vat) {
$vat->setSum($sum);
}
return $this->total = round($sum, 2);
}
/**
* Возвращает итоговую сумму чека. Тег ФФД - 1020.
*
* @return float
*/
public function getTotal(): float
{
return $this->total;
}
/**
* @inheritDoc
* @throws \Exception
*/
public function jsonSerialize()
{
$json = [
'company' => $this->getCompany()->jsonSerialize(), // обязательно
'payments' => $this->payments->jsonSerialize(), // обязательно
'cashier' => $this->getCashier() ?? '',
];
if ($this->getCorrectionInfo()) {
$json['correction_info'] = $this->getCorrectionInfo()->jsonSerialize(); // обязательно для коррекционных
} else {
$json['client'] = $this->getClient()->jsonSerialize(); // обязательно для некоррекционных
$json['items'] = $this->items->jsonSerialize(); // обязательно для некоррекционных
$json['total'] = $this->calcTotal(); // обязательно для некоррекционных
}
if ($this->getVats()) {
$json['vats'] = $this->vats->jsonSerialize();
}
return $json;
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинное имя кассира
*
* @package AtolOnline\Exceptions
*/
class AtolCashierTooLongException extends AtolException
{
/**
* AtolCashierTooLongException constructor.
*
* @param $name
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($name, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинное имя кассира (макс. длина 64, фактически '.strlen($name).'): '.$name;
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;
use Throwable;
/**
* Исключение, возникающее при попытке зарегистрировать документ без данных коррекции
*
* @package AtolOnline\Exceptions
*/
class AtolCorrectionInfoException extends AtolException
{
/**
* AtolNoCorrectionInfoException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'В документе отсутствуют данные коррекции';
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;
use Throwable;
/**
* Исключение, возникающее при попытке указать пустой email
*
* @package AtolOnline\Exceptions
*/
class AtolEmailEmptyException extends AtolException
{
/**
* AtolEmailEmptyException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Email не может быть пустым';
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинный email
*
* @package AtolOnline\Exceptions
*/
class AtolEmailTooLongException extends AtolException
{
/**
* AtolEmailTooLongException constructor.
*
* @param $email
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($email, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинный email (макс. длина '.$max.', фактически '.strlen($email).'): '.$email;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при ошибке валидации email
*
* @package AtolOnline\Exceptions
*/
class AtolEmailValidateException extends AtolException
{
/**
* AtolEmailValidateException constructor.
*
* @param $email
* @param string $message
* @param int $code
* @param \Throwable|null $previous
*/
public function __construct($email, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Некорректный email: '.$email;
parent::__construct($message, $code, $previous);
}
}

View File

@@ -1,22 +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;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
*
* @package AtolOnline\Exceptions
*/
class AtolException extends Exception
{
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать ИНН некорректной длины
*
* @package AtolOnline\Exceptions
*/
class AtolInnWrongLengthException extends AtolException
{
/**
* AtolInnWrongLengthException constructor.
*
* @param $inn
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($inn, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Длина ИНН должна быть 10 или 12 цифр, фактически '.strlen($inn).': '.$inn;
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;
use Throwable;
/**
* Исключение, возникающее при попытке указать пустой логин ККТ
*
* @package AtolOnline\Exceptions
*/
class AtolKktLoginEmptyException extends AtolException
{
/**
* AtolKktLoginEmptyException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Логин ККТ не может быть пустым';
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинный логин ККТ
*
* @package AtolOnline\Exceptions
*/
class AtolKktLoginTooLongException extends AtolException
{
/**
* AtolKktLoginTooLongException constructor.
*
* @param $login
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($login, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинный логин ККТ (макс. длина '.$max.', фактически '.strlen($login).'): '.$login;
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;
use Throwable;
/**
* Исключение, возникающее при попытке указать пустой пароль ККТ
*
* @package AtolOnline\Exceptions
*/
class AtolKktPasswordEmptyException extends AtolException
{
/**
* AtolKktPasswordEmptyException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Пароль ККТ не может быть пустым';
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинное имя
*
* @package AtolOnline\Exceptions
*/
class AtolNameTooLongException extends AtolException
{
/**
* AtolNameTooLongException constructor.
*
* @param $name
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($name, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинное имя/наименование (макс. длина '.$max.', фактически '.strlen($name).'): '.$name;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинный платёжный адрес
*
* @package AtolOnline\Exceptions
*/
class AtolPaymentAddressTooLongException extends AtolException
{
/**
* AtolPaymentAddressTooLongException constructor.
*
* @param $address
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($address, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинный адрес (макс. длина '.$max.', фактически '.strlen($address).'): '.$address;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинный телефон
*
* @package AtolOnline\Exceptions
*/
class AtolPhoneTooLongException extends AtolException
{
/**
* AtolPhoneTooLongException constructor.
*
* @param $phone
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($phone, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинный телефон (макс. длина '.$max.', фактически '.strlen($phone).'): '.$phone;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком высокую цену (сумму)
*
* @package AtolOnline\Exceptions
*/
class AtolPriceTooHighException extends AtolException
{
/**
* AtolPriceTooHighException constructor.
*
* @param $price
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($price, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком большая сумма (макс. '.$max.'): '.$price;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком большое количество
*
* @package AtolOnline\Exceptions
*/
class AtolQuantityTooHighException extends AtolException
{
/**
* AtolQuantityTooHighException 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 ?: 'Слишком большое количество (макс. '.$max.'): '.$quantity;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке добавить слишком много предметов расчёта в массив
*
* @package AtolOnline\Exceptions
*/
class AtolTooManyItemsException extends AtolException
{
/**
* AtolTooManyItemsException constructor.
*
* @param int $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком много предметов расчёта (макс. '.$max.')';
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке добавить слишком много ставок НДС в массив
*
* @package AtolOnline\Exceptions
*/
class AtolTooManyPaymentsException extends AtolException
{
/**
* AtolTooManyPaymentsException constructor.
*
* @param int $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком много платежей (макс. '.$max.')';
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке добавить слишком много ставок НДС в массив
*
* @package AtolOnline\Exceptions
*/
class AtolTooManyVatsException extends AtolException
{
/**
* AtolTooManyVatsException constructor.
*
* @param int $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком много ставок НДС (макс. '.$max.')';
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинный телефон
*
* @package AtolOnline\Exceptions
*/
class AtolUnitTooLongException extends AtolException
{
/**
* AtolUnitTooLongException constructor.
*
* @param $unit
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($unit, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинное название единицы измерения (макс. длина '.$max.', фактически '.strlen($unit).'): '.$unit;
parent::__construct($message, $code, $previous);
}
}

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;
use Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинный телефон
*
* @package AtolOnline\Exceptions
*/
class AtolUserdataTooLongException extends AtolException
{
/**
* AtolUserdataTooLongException constructor.
*
* @param $data
* @param $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($data, $max, $message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: 'Слишком длинный дополнительный реквизит (макс. длина '.$max.', фактически '.strlen($data).'): '.$data;
parent::__construct($message, $code, $previous);
}
}

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;
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)
{
$message = $message ?: 'Некорректный тип документа: ожидался \'receipt\' или \'correction\', указан \''.$type.'\'';
parent::__construct($message, $code, $previous);
}
}

View File

@@ -1,56 +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\{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 AtolEmailTooLongException
* @throws AtolEmailValidateException
*/
public function setEmail(string $email)
{
$email = trim($email);
if (strlen($email) > 64) {
throw new AtolEmailTooLongException($email, 64);
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new AtolEmailValidateException($email);
}
$this->email = $email;
return $this;
}
}

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\Traits;
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("/(^[0-9]{10}$)|(^[0-9]{12}$)/", $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,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\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_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а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/";
}

213
src/Entities/Client.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,
Exceptions\InvalidEmailException,
Exceptions\InvalidInnLengthException,
Exceptions\TooLongEmailException,
Exceptions\TooLongNameException,
Exceptions\TooLongPhoneException};
/**
* Класс 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 TooLongNameException Слишком длинное имя
* @throws TooLongPhoneException Слишком длинный телефон
* @throws TooLongEmailException Слишком длинный email
* @throws InvalidEmailException Невалидный email
* @throws InvalidInnLengthException Некорректная длина ИНН
*/
public function __construct(
?string $name = null,
?string $email = null,
?string $phone = null,
?string $inn = null
) {
$name && $this->setName($name);
$email && $this->setEmail($email);
$phone && $this->setPhone($phone);
$inn && $this->setInn($inn);
}
/**
* Возвращает наименование покупателя
*
* Тег ФФД - 1227
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Устанавливает наименование покупателя
*
* Тег ФФД - 1227
*
* @param string|null $name
* @return $this
* @throws TooLongNameException
*/
public function setName(?string $name): Client
{
if (is_string($name)) {
$name = preg_replace('/[\n\r\t]/', '', trim($name));
if (mb_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) {
throw new TooLongNameException($name, Constraints::MAX_LENGTH_CLIENT_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, Constraints::MAX_LENGTH_EMAIL);
} elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidEmailException($email);
}
}
$this->email = empty($email) ? null : $email;
return $this;
}
/**
* Возвращает установленный телефон
*
* Тег ФФД - 1008
*
* @return string|null
*/
public function getPhone(): ?string
{
return $this->phone;
}
/**
* Устанавливает телефон
*
* Тег ФФД - 1008
*
* @param string|null $phone Номер телефона
* @return $this
* @throws TooLongPhoneException
*/
public function setPhone(?string $phone): Client
{
if (is_string($phone)) {
$phone = preg_replace('/[^\d]/', '', trim($phone));
if (mb_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_PHONE) {
throw new TooLongPhoneException($phone, Constraints::MAX_LENGTH_CLIENT_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(): object
{
$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 (object)$json;
}
}

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

@@ -0,0 +1,227 @@
<?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\InvalidInnLengthException,
Exceptions\InvalidPaymentAddressException,
Exceptions\InvalidSnoException,
Exceptions\TooLongEmailException,
Exceptions\TooLongPaymentAddressException};
/**
* Класс, описывающий сущность компании-продавца
*
* @package AtolOnline\Entities
*/
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;
/**
* Company constructor.
*
* @param string $sno
* @param string $inn
* @param string $payment_address
* @param string $email
* @throws InvalidEmailException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
* @throws InvalidSnoException
* @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, Constraints::MAX_LENGTH_EMAIL);
} elseif (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidEmailException($email);
}
$this->email = $email;
return $this;
}
/**
* Возвращает установленный тип налогообложения
*
* Тег ФФД - 1055
*
* @return string
*/
public function getSno(): string
{
return $this->sno;
}
/**
* Устанавливает тип налогообложения
*
* Тег ФФД - 1055
*
* @param string $sno
* @return $this
* @throws InvalidSnoException
*/
public function setSno(string $sno): Company
{
$sno = trim($sno);
if (empty($sno) || !in_array($sno, SnoTypes::toArray())) {
throw new InvalidSnoException($sno);
}
$this->sno = $sno;
return $this;
}
/**
* Возвращает установленный ИНН
*
* Тег ФФД - 1018
*
* @return string
*/
public function getInn(): string
{
return $this->inn;
}
/**
* Устанавливает ИНН
*
* Тег ФФД - 1018
*
* @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;
}
/**
* Возвращает установленный адрес места расчётов
*
* Тег ФФД - 1187
*
* @return string
*/
public function getPaymentAddress(): string
{
return $this->payment_address;
}
/**
* Устанавливает адрес места расчётов
*
* Тег ФФД - 1187
*
* @param string $payment_address
* @return $this
* @throws TooLongPaymentAddressException
* @throws InvalidPaymentAddressException
*/
public function setPaymentAddress(string $payment_address): Company
{
$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, Constraints::MAX_LENGTH_PAYMENT_ADDRESS);
}
$this->payment_address = $payment_address;
return $this;
}
/**
* @inheritDoc
* @throws InvalidEmailException
* @throws InvalidSnoException
* @throws InvalidInnLengthException
* @throws InvalidPaymentAddressException
*/
public function jsonSerialize(): object
{
return (object)[
'email' => $this->email
? $this->getEmail()
: throw new InvalidEmailException(),
'sno' => $this->sno
? $this->getSno()
: throw new InvalidSnoException(),
'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(): object
{
return [
return (object)[
'type' => $this->getType() ?? '', // обязателен
'base_date' => $this->getDate() ?? '', // обязателен
'base_number' => $this->getNumber() ?? '', // обязателен
'base_name' => $this->getName() ?? '' // обязателен
];
}
}
}

468
src/Entities/Document.php Normal file
View File

@@ -0,0 +1,468 @@
<?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\AtolException;
use AtolOnline\Exceptions\BasicTooManyException;
use AtolOnline\Exceptions\InvalidEmailException;
use AtolOnline\Exceptions\InvalidInnLengthException;
use AtolOnline\Exceptions\InvalidJsonException;
use AtolOnline\Exceptions\TooHighPriceException;
use AtolOnline\Exceptions\TooLongCashierException;
use AtolOnline\Exceptions\TooLongEmailException;
use AtolOnline\Exceptions\TooLongNameException;
use AtolOnline\Exceptions\TooLongPaymentAddressException;
use AtolOnline\Exceptions\TooLongPhoneException;
use AtolOnline\Exceptions\TooLongUnitException;
use AtolOnline\Exceptions\TooLongUserdataException;
use AtolOnline\Exceptions\TooManyItemsException;
use AtolOnline\Exceptions\TooManyPaymentsException;
use AtolOnline\Exceptions\TooManyVatsException;
use Exception;
/**
* Класс, описывающий документ
*
* @package AtolOnline\Entities
*/
class Document extends Entity
{
/**
* @var ItemArray Массив предметов расчёта
*/
protected ItemArray $items;
/**
* @var VatArray Массив ставок НДС
*/
protected VatArray $vats;
/**
* @var PaymentArray Массив оплат
*/
protected PaymentArray $payments;
/**
* @var Company Объект компании (продавца)
*/
protected Company $company;
/**
* @var Client Объект клиента (покупателя)
*/
protected Client $client;
/**
* @var float Итоговая сумма чека. Тег ФФД - 1020.
*/
protected float $total = 0;
/**
* @var string ФИО кассира. Тег ФФД - 1021.
*/
protected string $cashier;
/**
* @var CorrectionInfo Данные коррекции
*/
protected CorrectionInfo $correction_info;
/**
* Document constructor.
*/
public function __construct()
{
$this->vats = new VatArray();
$this->payments = new PaymentArray();
$this->items = new ItemArray();
}
/**
* Удаляет все налоги из документа и предметов расчёта
*
* @return $this
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function clearVats(): Document
{
$this->setVats([]);
return $this;
}
/**
* Добавляет новую ставку НДС в массив ставок НДС
*
* @param Vat $vat Объект ставки НДС
* @return $this
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function addVat(Vat $vat): Document
{
$this->vats->add($vat);
return $this;
}
/**
* Возвращает массив ставок НДС
*
* @return Vat[]
*/
public function getVats(): array
{
return $this->vats->get();
}
/**
* Устанавливает массив ставок НДС
*
* @param Vat[] $vats Массив ставок НДС
* @return $this
* @throws TooManyVatsException Слишком много ставок НДС
* @throws Exception
*/
public function setVats(array $vats): Document
{
$this->vats->set($vats);
return $this;
}
/**
* Добавляет новую оплату в массив оплат
*
* @param Payment $payment Объект оплаты
* @return $this
* @throws Exception
* @throws TooManyPaymentsException Слишком много оплат
*/
public function addPayment(Payment $payment): Document
{
if (count($this->getPayments()) == 0 && !$payment->getSum()) {
$payment->setSum($this->calcTotal());
}
$this->payments->add($payment);
return $this;
}
/**
* Возвращает массив оплат
*
* @return Payment[]
*/
public function getPayments(): array
{
return $this->payments->get();
}
/**
* Устанавливает массив оплат
*
* @param Payment[] $payments Массив оплат
* @return $this
* @throws TooManyPaymentsException Слишком много оплат
*/
public function setPayments(array $payments): Document
{
$this->payments->set($payments);
return $this;
}
/**
* Добавляет новый предмет расчёта в массив предметов расчёта
*
* @param Item $item Объект предмета расчёта
* @return $this
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function addItem(Item $item): Document
{
$this->items->add($item);
return $this;
}
/**
* Возвращает массив предметов расчёта
*
* @return Item[]
*/
public function getItems(): array
{
return $this->items->get();
}
/**
* Устанавливает массив предметов расчёта
*
* @param Item[] $items Массив предметов расчёта
* @return $this
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function setItems(array $items): Document
{
$this->items->set($items);
return $this;
}
/**
* Возвращает заданного клиента (покупателя)
*
* @return Client|null
*/
public function getClient(): ?Client
{
return $this->client;
}
/**
* Устанавливает клиента (покупателя)
*
* @param Client|null $client
* @return $this
*/
public function setClient(?Client $client): Document
{
$this->client = $client;
return $this;
}
/**
* Возвращает заданную компанию (продавца)
*
* @return Company|null
*/
public function getCompany(): ?Company
{
return $this->company;
}
/**
* Устанавливает компанию (продавца)
*
* @param Company|null $company
* @return $this
*/
public function setCompany(?Company $company): Document
{
$this->company = $company;
return $this;
}
/**
* Возвращает ФИО кассира. Тег ФФД - 1021.
*
* @return string|null
*/
public function getCashier(): ?string
{
return $this->cashier;
}
/**
* Устанавливает ФИО кассира. Тег ФФД - 1021.
*
* @param string|null $cashier
* @return $this
* @throws TooLongCashierException
*/
public function setCashier(?string $cashier): Document
{
if ($cashier !== null) {
$cashier = trim($cashier);
if (mb_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
throw new TooLongCashierException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME);
}
}
$this->cashier = $cashier;
return $this;
}
/**
* Возвращает данные коррекции
*
* @return CorrectionInfo|null
*/
public function getCorrectionInfo(): ?CorrectionInfo
{
return $this->correction_info;
}
/**
* Устанавливает данные коррекции
*
* @param CorrectionInfo|null $correction_info
* @return $this
*/
public function setCorrectionInfo(?CorrectionInfo $correction_info): Document
{
$this->correction_info = $correction_info;
return $this;
}
/**
* Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020.
*
* @return float
* @throws Exception
*/
public function calcTotal(): float
{
$sum = 0;
$this->clearVats();
foreach ($this->items->get() as $item) {
$sum += $item->calcSum();
$this->addVat(new Vat($item->getVat()->getType(), $item->getSum()));
}
return $this->total = round($sum, 2);
}
/**
* Возвращает итоговую сумму чека. Тег ФФД - 1020.
*
* @return float
*/
public function getTotal(): float
{
return $this->total;
}
/**
* Собирает объект документа из сырой json-строки
*
* @param string $json
* @return Document
* @throws TooLongEmailException
* @throws InvalidEmailException
* @throws AtolException
* @throws InvalidInnLengthException
* @throws InvalidJsonException
* @throws TooLongNameException
* @throws TooLongPaymentAddressException
* @throws TooLongPhoneException
* @throws TooHighPriceException
* @throws BasicTooManyException
* @throws TooManyItemsException
* @throws TooManyPaymentsException
* @throws TooLongUnitException
* @throws TooLongUserdataException
* @throws Exception
*/
public static function fromRaw(string $json): Document
{
$array = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidJsonException();
}
$doc = new self();
if (isset($array['company'])) {
$doc->setCompany(new Company(
$array['company']['sno'] ?? null,
$array['company']['inn'] ?? null,
$array['company']['payment_address'] ?? null,
$array['company']['email'] ?? null
));
}
if (isset($array['client'])) {
$doc->setClient(new Client(
$array['client']['name'] ?? null,
$array['client']['phone'] ?? null,
$array['client']['email'] ?? null,
$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(
$ar_item['name'] ?? null,
$ar_item['price'] ?? null,
$ar_item['quantity'] ?? null,
$ar_item['measurement_unit'] ?? null,
$ar_item['vat']['type'] ?? null,
$ar_item['payment_object'] ?? null,
$ar_item['payment_method'] ?? null
);
if (!empty($ar_item['user_data'])) {
$item->setUserData($ar_item['user_data'] ?? null);
}
$doc->addItem($item);
}
}
if (isset($array['payments'])) {
foreach ($array['payments'] as $ar_payment) {
$payment = new Payment();
if (isset($ar_payment['type'])) {
$payment->setType($ar_payment['type']);
}
if (isset($ar_payment['sum'])) {
$payment->setSum($ar_payment['sum']);
}
$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
*/
public function jsonSerialize(): array
{
if ($this->getCompany()) {
$json['company'] = $this->getCompany()->jsonSerialize(); // обязательно
}
if ($this->getPayments()) {
$json['payments'] = $this->payments->jsonSerialize(); // обязательно
}
if ($this->getCashier()) {
$json['cashier'] = $this->getCashier();
}
if ($this->getCorrectionInfo()) {
$json['correction_info'] = $this->getCorrectionInfo()->jsonSerialize(); // обязательно для коррекционных
} else {
if ($this->getClient()) {
$json['client'] = $this->getClient()->jsonSerialize(); // обязательно для некоррекционных
}
if ($this->getItems()) {
$json['items'] = $this->items->jsonSerialize(); // обязательно для некоррекционных
}
$json['total'] = $this->calcTotal(); // обязательно для некоррекционных
}
if ($this->getVats()) {
$json['vats'] = $this->vats->jsonSerialize();
}
return $json;
}
}

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,21 +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\Entities;
use AtolOnline\{Exceptions\AtolNameTooLongException,
Exceptions\AtolPriceTooHighException,
Exceptions\AtolQuantityTooHighException,
Exceptions\AtolUnitTooLongException,
Exceptions\AtolUserdataTooLongException,
Traits\RublesKopeksConverter
};
use AtolOnline\{
Constants\Constraints,
Exceptions\BasicTooManyException,
Exceptions\TooHighPriceException,
Exceptions\TooLongNameException,
Exceptions\TooLongUnitException,
Exceptions\TooLongUserdataException};
/**
* Предмет расчёта (товар, услуга)
@@ -24,74 +26,72 @@ use AtolOnline\{Exceptions\AtolNameTooLongException,
*/
class Item extends Entity
{
use RublesKopeksConverter;
/**
* @var string Наименование. Тег ФФД - 1030.
*/
protected $name;
protected string $name;
/**
* @var int Цена в копейках (с учётом скидок и наценок). Тег ФФД - 1079.
*/
protected $price = 0;
protected int $price = 0;
/**
* @var float Количество, вес. Тег ФФД - 1023.
*/
protected $quantity = 0.0;
protected float $quantity = 0.0;
/**
* @var float Сумма в копейках. Тег ФФД - 1043.
*/
protected $sum = 0;
protected float $sum = 0;
/**
* @var string Единица измерения количества. Тег ФФД - 1197.
*/
protected $measurement_unit;
protected string $measurement_unit;
/**
* @var Vat Ставка НДС
* @var Vat|null Ставка НДС
*/
protected $vat;
protected ?Vat $vat;
/**
* @var string Признак способа расчёта. Тег ФФД - 1214.
*/
protected $payment_method;
protected string $payment_method;
/**
* @var string Признак объекта расчёта. Тег ФФД - 1212.
*/
protected $payment_object;
protected string $payment_object;
/**
* @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 AtolQuantityTooHighException Слишком большое количество
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
* @param string|null $vat_type Ставка НДС
* @param string|null $payment_object Признак
* @param string|null $payment_method Способ расчёта
* @throws TooLongNameException Слишком длинное наименование
* @throws TooHighPriceException Слишком высокая цена за одну единицу
* @throws BasicTooManyException Слишком большое количество
* @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
) {
@@ -101,17 +101,17 @@ class Item extends Entity
if ($price) {
$this->setPrice($price);
}
if ($payment_object) {
$this->setPaymentObject($payment_object);
}
if ($quantity) {
$this->setQuantity($quantity);
}
if ($measurement_unit) {
$this->setMeasurementUnit($measurement_unit);
}
if ($vat_type) {
$this->setVatType($vat_type);
}
if ($measurement_unit) {
$this->setMeasurementUnit($measurement_unit);
if ($payment_object) {
$this->setPaymentObject($payment_object);
}
if ($payment_method) {
$this->setPaymentMethod($payment_method);
@@ -123,23 +123,23 @@ class Item extends Entity
*
* @return string
*/
public function getName()
public function getName(): string
{
return $this->name;
}
/**
* Устаналивает наименование. Тег ФФД - 1030.
*
* @param string $name Наименование
* @return $this
* @throws AtolNameTooLongException Слишком длинное имя/наименование
* @throws TooLongNameException Слишком длинное имя/наименование
*/
public function setName(string $name)
public function setName(string $name): self
{
$name = trim($name);
if (strlen($name) > 128) {
throw new AtolNameTooLongException($name, 128);
if (mb_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) {
throw new TooLongNameException($name, Constraints::MAX_LENGTH_ITEM_NAME);
}
$this->name = $name;
return $this;
@@ -150,24 +150,24 @@ class Item extends Entity
*
* @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;
}
@@ -181,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 AtolQuantityTooHighException Слишком большое количество
* @throws AtolPriceTooHighException Слишком высокая общая стоимость
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
* @throws BasicTooManyException Слишком большое количество
* @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 AtolQuantityTooHighException($quantity, 99999.999);
throw new BasicTooManyException($quantity, 99999.999);
}
$this->quantity = $quantity;
$this->calcSum();
@@ -215,19 +215,19 @@ 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 (strlen($measurement_unit) > 16) {
throw new AtolUnitTooLongException($measurement_unit, 16);
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;
@@ -250,7 +250,7 @@ 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;
@@ -273,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
@@ -318,19 +318,19 @@ 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 (strlen($user_data) > 64) {
throw new AtolUserdataTooLongException($user_data, 64);
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;
@@ -343,24 +343,24 @@ 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();
}

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\Exceptions\AtolTooManyItemsException;
use AtolOnline\Exceptions\TooManyItemsException;
/**
* Класс, описывающий массив предметов расчёта
@@ -28,13 +30,13 @@ class ItemArray extends Entity
/**
* @var Item[] Массив предметов расчёта
*/
private $items = [];
private array $items = [];
/**
* ItemArray constructor.
*
* @param Item[]|null $items Массив предметов расчёта
* @throws AtolTooManyItemsException Слишком много предметов расчёта
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function __construct(?array $items = null)
{
@@ -42,30 +44,30 @@ class ItemArray extends Entity
$this->set($items);
}
}
/**
* Устанавливает массив предметов расчёта
*
* @param Item[] $items Массив предметов расчёта
* @return $this
* @throws AtolTooManyItemsException Слишком много предметов расчёта
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function set(array $items)
public function set(array $items): ItemArray
{
if ($this->validateCount($items)) {
$this->items = $items;
}
return $this;
}
/**
* Добавляет предмет расчёта в массив
*
* @param Item $item Объект предмета расчёта
* @return $this
* @throws AtolTooManyItemsException Слишком много предметов расчёта
* @throws TooManyItemsException Слишком много предметов расчёта
*/
public function add(Item $item)
public function add(Item $item): ItemArray
{
if ($this->validateCount()) {
$this->items[] = $item;
@@ -78,7 +80,7 @@ class ItemArray extends Entity
*
* @return Item[]
*/
public function get()
public function get(): array
{
return $this->items;
}
@@ -94,19 +96,19 @@ class ItemArray extends Entity
}
return $result;
}
/**
* Проверяет количество предметов расчёта
*
* @param Item[]|null $items Если передать массив, то проверит количество его элементов.
* Иначе проверит количество уже присвоенных элементов.
* @return bool true если всё хорошо, иначе выбрасывает исключение
* @throws AtolTooManyItemsException Слишком много предметов расчёта
* @throws TooManyItemsException Слишком много предметов расчёта
*/
protected function validateCount(?array $items = null): bool
{
if ((!empty($items) && count($items) >= self::MAX_COUNT) || count($this->items) >= self::MAX_COUNT) {
throw new AtolTooManyItemsException(self::MAX_COUNT);
throw new TooManyItemsException(count($items), self::MAX_COUNT);
}
return true;
}

149
src/Entities/Kkt.php Normal file
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
*/
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;
}
/**
* Возвращает объект с информацией о сетевой ошибке
*
* @return object
*/
public function getNetworkError(): object
{
return (object)[
'code' => $this->data->networkErrorCode,
'text' => self::ERROR_CODES[$this->data->networkErrorCode],
];
}
/**
* @inheritDoc
*/
public function jsonSerialize()
{
return $this->data;
}
}

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

@@ -1,16 +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\Api\SellSchema;
use AtolOnline\Exceptions\AtolTooManyPaymentsException;
use AtolOnline\Exceptions\TooManyPaymentsException;
/**
* Класс, описывающий массив оплат
@@ -19,16 +20,21 @@ use AtolOnline\Exceptions\AtolTooManyPaymentsException;
*/
class PaymentArray extends Entity
{
/**
* Максимальное количество элементов массива
*/
public const MAX_COUNT = 10;
/**
* @var Payment[] Массив оплат
*/
private $payments = [];
private array $payments = [];
/**
* ItemArray constructor.
*
* @param Payment[]|null $payments Массив оплат
* @throws AtolTooManyPaymentsException Слишком много оплат
* @throws TooManyPaymentsException Слишком много оплат
*/
public function __construct(?array $payments = null)
{
@@ -36,30 +42,30 @@ class PaymentArray extends Entity
$this->set($payments);
}
}
/**
* Устанавливает массив оплат
*
* @param Payment[] $payments
* @return $this
* @throws AtolTooManyPaymentsException Слишком много оплат
* @throws TooManyPaymentsException Слишком много оплат
*/
public function set(array $payments)
public function set(array $payments): PaymentArray
{
if ($this->validateCount($payments)) {
$this->payments = $payments;
}
return $this;
}
/**
* Добавляет новую оплату к заданным
*
* @param Payment $payment Объект оплаты
* @return $this
* @throws AtolTooManyPaymentsException Слишком много оплат
* @throws TooManyPaymentsException Слишком много оплат
*/
public function add(Payment $payment)
public function add(Payment $payment): PaymentArray
{
if ($this->validateCount()) {
$this->payments[] = $payment;
@@ -72,7 +78,7 @@ class PaymentArray extends Entity
*
* @return Payment[]
*/
public function get()
public function get(): array
{
return $this->payments;
}
@@ -88,20 +94,19 @@ class PaymentArray extends Entity
}
return $result;
}
/**
* Проверяет количество налоговых ставок
*
* @param Payment[]|null $payments Если передать массив, то проверит количество его элементов.
* Иначе проверит количество уже присвоенных элементов.
* @return bool true если всё хорошо, иначе выбрасывает исключение
* @throws AtolTooManyPaymentsException Слишком много оплат
* @throws TooManyPaymentsException Слишком много оплат
*/
protected function validateCount(?array $payments = null): bool
{
$max_items = SellSchema::get()->properties->receipt->properties->payments->maxItems;
if ((!empty($payments) && count($payments) >= $max_items) || count($this->payments) >= $max_items) {
throw new AtolTooManyPaymentsException($max_items);
if ((!empty($payments) && count($payments) >= self::MAX_COUNT) || count($this->payments) >= self::MAX_COUNT) {
throw new TooManyPaymentsException(count($payments), self::MAX_COUNT);
}
return true;
}

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\VatTypes, Traits\RublesKopeksConverter};
use AtolOnline\Enums\VatTypes;
/**
* Класс, описывающий ставку НДС
@@ -18,22 +20,20 @@ use AtolOnline\{Constants\VatTypes, Traits\RublesKopeksConverter};
*/
class Vat extends Entity
{
use RublesKopeksConverter;
/**
* @var string Выбранный тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
*/
private $type;
private string $type;
/**
* @var int Сумма в копейках, от которой пересчитывается размер налога
* @var int Сумма в копейках, от которой пересчитывается размер НДС
*/
private $sum_original = 0;
private int $sum_original = 0;
/**
* @var int Сумма налога в копейках
* @var int Сумма НДС в копейках
*/
private $sum_final = 0;
private int $sum_final = 0;
/**
* Vat constructor.
@@ -50,7 +50,8 @@ class Vat extends Entity
}
/**
* Устанавливает размер НДС от суммы в копейках
* Устанавливает:
* размер НДС от суммы в копейках
*
* @param string $type Тип ставки НДС
* @param int $kopeks Копейки
@@ -66,18 +67,19 @@ class Vat extends Entity
case VatTypes::VAT0:
return 0;
case VatTypes::VAT10:
return $kopeks * 10 / 100;
//return $kopeks * 10 / 100;
case VatTypes::VAT110:
return $kopeks * 10 / 110;
case VatTypes::VAT18:
return $kopeks * 18 / 100;
//return $kopeks * 18 / 100;
case VatTypes::VAT118:
return $kopeks * 18 / 118;
case VatTypes::VAT20:
return $kopeks * 20 / 100;
//return $kopeks * 20 / 100;
case VatTypes::VAT120:
return $kopeks * 20 / 120;
}
return 0;
}
/**
@@ -97,7 +99,7 @@ class Vat extends Entity
* @param string $type Тип ставки НДС
* @return $this
*/
public function setType(string $type)
public function setType(string $type): Vat
{
$this->type = $type;
$this->setFinal();
@@ -109,9 +111,9 @@ class Vat extends Entity
*
* @return float
*/
public function getFinalSum()
public function getFinalSum(): float
{
return self::toRub($this->sum_final);
return rubles($this->sum_final);
}
/**
@@ -121,9 +123,9 @@ class Vat extends Entity
* @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС
* @return $this
*/
public function setSum(float $rubles)
public function setSum(float $rubles): Vat
{
$this->sum_original = self::toKop($rubles);
$this->sum_original = kopeks($rubles);
$this->setFinal();
return $this;
}
@@ -135,7 +137,7 @@ class Vat extends Entity
*/
public function getSum(): float
{
return self::toRub($this->sum_original);
return rubles($this->sum_original);
}
/**
@@ -145,9 +147,9 @@ class Vat extends Entity
* @param float $rubles
* @return $this
*/
public function addSum(float $rubles)
public function addSum(float $rubles): Vat
{
$this->sum_original += self::toKop($rubles);
$this->sum_original += kopeks($rubles);
$this->setFinal();
return $this;
}
@@ -161,7 +163,7 @@ class Vat extends Entity
*/
public function calc(float $rubles): float
{
return self::toRub(self::calculator($this->type, self::toKop($rubles)));
return rubles(self::calculator($this->type, kopeks($rubles)));
}
/**
@@ -178,7 +180,7 @@ class Vat extends Entity
/**
* Расчитывает и устанавливает итоговый размер ставки от исходной суммы в копейках
*/
protected function setFinal()
protected function setFinal(): Vat
{
$this->sum_final = self::calculator($this->type, $this->sum_original);
return $this;

View File

@@ -1,16 +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\Api\SellSchema;
use AtolOnline\Exceptions\AtolTooManyVatsException;
use AtolOnline\Exceptions\TooManyVatsException;
/**
* Класс, описывающий массив ставок НДС
@@ -19,16 +20,21 @@ use AtolOnline\Exceptions\AtolTooManyVatsException;
*/
class VatArray extends Entity
{
/**
* Максимальное количество элементов массива
*/
public const MAX_COUNT = 6;
/**
* @var Vat[] Массив ставок НДС
*/
private $vats = [];
private array $vats = [];
/**
* VatArray constructor.
*
* @param Vat[]|null $vats Массив ставок НДС
* @throws AtolTooManyVatsException Слишком много ставок НДС
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function __construct(?array $vats = null)
{
@@ -36,33 +42,37 @@ class VatArray extends Entity
$this->set($vats);
}
}
/**
* Устанавливает массив ставок НДС
*
* @param Vat[] $vats Массив ставок НДС
* @return $this
* @throws AtolTooManyVatsException Слишком много ставок НДС
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function set(array $vats)
public function set(array $vats): VatArray
{
if ($this->validateCount($vats)) {
$this->vats = $vats;
}
return $this;
}
/**
* Добавляет новую ставку НДС в массив
*
* @param Vat $vat Объект ставки НДС
* @return $this
* @throws AtolTooManyVatsException Слишком много ставок НДС
* @throws TooManyVatsException Слишком много ставок НДС
*/
public function add(Vat $vat)
public function add(Vat $vat): VatArray
{
if ($this->validateCount()) {
$this->vats[] = $vat;
if (isset($this->vats[$vat->getType()])) {
$this->vats[$vat->getType()]->addSum($vat->getSum());
} else {
$this->vats[$vat->getType()] = $vat;
}
}
return $this;
}
@@ -72,7 +82,7 @@ class VatArray extends Entity
*
* @return Vat[]
*/
public function get()
public function get(): array
{
return $this->vats;
}
@@ -88,20 +98,19 @@ class VatArray extends Entity
}
return $result;
}
/**
* Проверяет количество налоговых ставок
*
* @param Vat[]|null $vats Если передать массив, то проверит количество его элементов.
* Иначе проверит количество уже присвоенных элементов.
* @return bool true если всё хорошо, иначе выбрасывает исключение
* @throws AtolTooManyVatsException Слишком много ставок НДС
* @throws TooManyVatsException Слишком много ставок НДС
*/
protected function validateCount(?array $vats = null): bool
{
$max_items = SellSchema::get()->properties->receipt->properties->vats->maxItems;
if ((!empty($vats) && count($vats) >= $max_items) || count($this->vats) >= $max_items) {
throw new AtolTooManyVatsException($max_items);
if ((!empty($vats) && count($vats) >= self::MAX_COUNT) || count($this->vats) >= self::MAX_COUNT) {
throw new TooManyVatsException(count($vats), self::MAX_COUNT);
}
return true;
}

View File

@@ -1,28 +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
*/
namespace AtolOnline\Constants;
declare(strict_types = 1);
namespace AtolOnline\Enums;
use MyCLabs\Enum\Enum;
/**
* Константы, определяющие типы документов коррекции
*
* @package AtolOnline\Constants
* Тег ФФД - 1173
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35 (correction_info)
*/
class CorrectionTypes
final class CorrectionTypes extends Enum
{
/**
* Самостоятельно
*/
const SELF = 'self';
/**
* По предписанию
*/
const INSTRUCTION = 'instruction';
}
}

View File

@@ -0,0 +1,30 @@
<?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 MyCLabs\Enum\Enum;
/**
* Константы, определяющие типы документов
*/
final class DocumentTypes extends Enum
{
/**
* Чек прихода, возврата прихода, расхода, возврата расхода
*/
const RECEIPT = 'receipt';
/**
* Чек коррекции
*/
const CORRECTION = 'correction';
}

View File

@@ -1,54 +1,59 @@
<?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;
/**
* Константы, определяющие признаки способов расчёта. Тег ФФД - 1214.
* Константы, определяющие признаки способов расчёта
*
* @package AtolOnline\Constants
* Тег ФФД - 1214
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 22 (payment_method)
*/
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';
}

View File

@@ -1,108 +1,161 @@
<?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;
/**
* Константы, определяющие признаки предметов расчёта. Тег ФФД - 1212.
* Константы, определяющие признаки предметов расчёта
*
* @package AtolOnline\Constants
* Тег ФФД - 1212
*
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 23 (payment_object)
*/
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';
}

View File

@@ -0,0 +1,77 @@
<?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 MyCLabs\Enum\Enum;
/**
* Константы, определяющие виды оплат
*
* Теги ФФД: 1031, 1081, 1215, 1216, 1217
*/
final class PaymentTypes extends Enum
{
/**
* Расчёт наличными. Тег ФФД - 1031.
*/
const CASH = 0;
/**
* Расчёт безналичными. Тег ФФД - 1081.
*/
const ELECTRON = 1;
/**
* Предварительная оплата (зачёт аванса). Тег ФФД - 1215.
*/
const PRE_PAID = 2;
/**
* Предварительная оплата (кредит). Тег ФФД - 1216.
*/
const CREDIT = 3;
/**
* Иная форма оплаты (встречное предоставление). Тег ФФД - 1217.
*/
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;
}

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,46 +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
* Тег ФДД - 1055
*/
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';
/**
* Патентная СН
*/

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,52 @@
<?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;
use Throwable;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
*/
class AtolException extends Exception
{
/**
* @var int[] Теги ФФД
*/
protected array $ffd_tags = [];
/**
* AtolException constructor.
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: $this->message;
if ($this->getFfdTags()) {
$message .= ' [FFD tags: '.implode(', ', $this->getFfdTags()).']';
}
parent::__construct($message, $code, $previous);
}
/**
* Возвращает теги ФФД, с которыми связано исключение
*
* @return array
*/
protected function getFfdTags(): array
{
return $this->ffd_tags;
}
}

View File

@@ -0,0 +1,41 @@
<?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;
use Throwable;
/**
* Исключение, возникающее при неудачной авторизации
*/
class AuthFailedException extends Exception
{
/**
* Конструктор
*
* @param KktResponse $response
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(KktResponse $response, $message = "", $code = 0, Throwable $previous = null)
{
$message = $response->isValid()
? $message
: '[' . $response->error->code . '] ' . $response->error->text .
'. ERROR_ID: ' . $response->error->error_id .
'. TYPE: ' . $response->error->type;
$code = $response->isValid() ? $code : $response->error->code;
parent::__construct($message, $code, $previous);
}
}

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 Throwable;
/**
* Исключение, возникающее при попытке указать слишком длинное что-либо
*/
class BasicTooLongException 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 = "", int $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: $this->message . ' (max length - ' . $max . ', actual length - ' .
mb_strlen($string), $code, $previous);
}
}

View File

@@ -0,0 +1,43 @@
<?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 Throwable;
/**
* Исключение, возникающее при попытке указать слишком большое количество чего-либо
*/
class BasicTooManyException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Quantity is too high';
/**
* AtolTooManyException constructor.
*
* @param float $quantity
* @param float $max
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(float $quantity, float $max, $message = "", int $code = 0, Throwable $previous = null)
{
parent::__construct(
$message ?: $this->message . ' (max - ' . $max . ', actual - ' . $quantity . ')',
$code,
$previous
);
}
}

View File

@@ -0,0 +1,23 @@
<?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 EmptyCorrectionInfoException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Document must have correction info';
}

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;
/**
* Исключение, возникающее при попытке указать пустой email
*/
class EmptyEmailException extends AtolException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1008,
1117,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Email cannot be empty';
}

View File

@@ -0,0 +1,23 @@
<?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 EmptyLoginException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT login cannot be empty';
}

View File

@@ -0,0 +1,23 @@
<?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
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Cannot create KKT entity without data from monitor';
}

View File

@@ -0,0 +1,23 @@
<?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 EmptyPasswordException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT password cannot be empty';
}

View File

@@ -0,0 +1,23 @@
<?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;
/**
* Исключение, возникающее при попытке указать невалидный callback_url
*/
class InvalidCallbackUrlException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Invalid callback URL';
}

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\DocumentTypes;
use Throwable;
/**
* Исключение, возникающее при попытке указать некорректный тип документа
*/
class InvalidDocumentTypeException extends AtolException
{
/**
* Конструктор
*
* @param string $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: '$type'. Valid ones: " . implode(', ', DocumentTypes::toArray()),
$code,
$previous
);
}
}

View File

@@ -0,0 +1,41 @@
<?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 Throwable;
/**
* Исключение, возникающее при ошибке валидации email
*/
class InvalidEmailException extends AtolException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1008,
1117,
];
/**
* AtolEmailValidateException constructor.
*
* @param string $email
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $email = '', $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: "Invalid email: '$email'", $code, $previous);
}
}

View File

@@ -0,0 +1,47 @@
<?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 Throwable;
/**
* Исключение, возникающее при попытке указать ИНН некорректной длины
*/
class InvalidInnLengthException extends AtolException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1016,
1018,
1226,
1228,
];
/**
* AtolInnWrongLengthException constructor.
*
* @param string $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, actual is ' . strlen($inn),
$code,
$previous
);
}
}

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\Exceptions;
use Throwable;
/**
* Исключение, возникающее при работе с невалидным JSON
*/
class InvalidJsonException 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

@@ -0,0 +1,44 @@
<?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 Throwable;
/**
* Исключение, возникающее при попытке указать некорректный платёжный адрес
*/
class InvalidPaymentAddressException extends AtolException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1187,
];
/**
* Конструктор
*
* @param string $address
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($address = '', $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct(
$message ?: "Wrong payment address: '$address'",
$code,
$previous
);
}
}

View File

@@ -0,0 +1,45 @@
<?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\SnoTypes;
use Throwable;
/**
* Исключение, возникающее при попытке указать некорректную СНО
*/
class InvalidSnoException extends AtolException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1055,
];
/**
* Конструктор
*
* @param string $sno
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($sno = '', $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct(
$message ?: "Wrong SNO: '$sno'. Valid ones: " . implode(', ', SnoTypes::toArray()),
$code,
$previous
);
}
}

View File

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

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 Throwable;
/**
* Исключение, возникающее при попытке создать объект ККТ с неполными данными от монитора
*/
class NotEnoughMonitorDataException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Cannot create KKT entity without these properties: ';
/**
* Конструктор
*
* @param array $props_diff
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(array $props_diff, $message = "", int $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: $this->message . implode(', ', $props_diff), $code, $previous);
}
}

View File

@@ -0,0 +1,30 @@
<?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 TooHighPriceException extends BasicTooManyException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1079,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Price is too high';
}

View File

@@ -0,0 +1,23 @@
<?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;
/**
* Исключение, возникающее при попытке указать слишком длинный callback_url
*/
class TooLongCallbackUrlException extends BasicTooLongException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Callback URL is too long';
}

View File

@@ -0,0 +1,30 @@
<?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 TooLongCashierException extends BasicTooLongException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1021,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Cashier name is too long';
}

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;
/**
* Исключение, возникающее при попытке указать слишком длинный email
*/
class TooLongEmailException extends BasicTooLongException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1008,
1117,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Email is too long';
}

View File

@@ -0,0 +1,23 @@
<?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 TooLongLoginException extends BasicTooLongException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT login is too long';
}

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 TooLongNameException extends BasicTooLongException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1026,
1030,
1085,
1225,
1227,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Name is too long';
}

View File

@@ -0,0 +1,23 @@
<?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 TooLongPasswordException extends BasicTooLongException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT password is too long';
}

View File

@@ -0,0 +1,30 @@
<?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 TooLongPaymentAddressException extends AtolException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1187,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Payment address is too long';
}

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 TooLongPhoneException extends BasicTooLongException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1008,
1073,
1074,
1075,
1171,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Phone is too long';
}

View File

@@ -0,0 +1,30 @@
<?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 TooLongUnitException extends BasicTooLongException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1197,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Measurement unit is too long';
}

View File

@@ -0,0 +1,30 @@
<?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 TooLongUserdataException extends BasicTooLongException
{
/**
* @inheritDoc
*/
protected array $ffd_tags = [
1191,
];
/**
* @var string Сообщение об ошибке
*/
protected $message = 'User data is too long';
}

View File

@@ -0,0 +1,23 @@
<?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 TooManyItemsException extends BasicTooManyException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Too many items';
}

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