mirror of
https://github.com/anthonyaxenov/atol-online.git
synced 2024-11-22 11:34:33 +00:00
Compare commits
87 Commits
cc944a1dbb
...
baf97cad20
Author | SHA1 | Date | |
---|---|---|---|
baf97cad20 | |||
2e8099e0a4 | |||
d7f3c81fac | |||
e22c1cb091 | |||
58bc344a86 | |||
fdc5ab112a | |||
71d1f2900c | |||
b4cc0fec53 | |||
573af15bac | |||
19653776c5 | |||
c7d07a18f1 | |||
464a8f0706 | |||
294a3ef2f3 | |||
b4af189292 | |||
6787ce3ad7 | |||
1d6abfd475 | |||
058ce5ed3d | |||
16d1146826 | |||
703c5178f5 | |||
fdc64954f9 | |||
793549aaac | |||
b57acf8b05 | |||
b39e76f312 | |||
a34a6927d1 | |||
1f3d5d2f3d | |||
359264db64 | |||
557c76fefa | |||
bf09641c8b | |||
6b5a025051 | |||
3d3eba5b4e | |||
a5c88cd7d3 | |||
b451c7dc68 | |||
7d0c526663 | |||
65ec639014 | |||
d533164d1b | |||
05fd25e810 | |||
3e03fdca61 | |||
c077f98cf9 | |||
2260233e3f | |||
c30c7d069f | |||
8f235d4730 | |||
85750cd211 | |||
2a66889e46 | |||
1c0d8ba64d | |||
fc37580078 | |||
96137d20b2 | |||
267431ec28 | |||
cb24bb1fb0 | |||
11646113b6 | |||
650b46923e | |||
3f3eb68ea2 | |||
5ccb0e9db4 | |||
bce21f9658 | |||
a7205ff754 | |||
e0ff5a261a | |||
2ebb172f2e | |||
e2141551d5 | |||
c9670a1321 | |||
9d2617858d | |||
f548032843 | |||
e0d792d3a4 | |||
16c8d8a676 | |||
8b79b2be51 | |||
95dbc3a5b7 | |||
790831e933 | |||
3b75c8b983 | |||
c5b57ec26d | |||
42d194116f | |||
b5a01debd2 | |||
9d3344a0df | |||
00b2643f42 | |||
3bf8667437 | |||
e1303f4fdb | |||
920c08c610 | |||
d281071970 | |||
95499174b0 | |||
6d0cac2cbc | |||
6620acf1bf | |||
e89369348a | |||
b35b9bfa87 | |||
e1120051c1 | |||
6551366d84 | |||
2c5144caac | |||
92a2c6cc48 | |||
949b31a85a | |||
03591600dd | |||
77481884ad |
6
.github/FUNDING.yml
vendored
6
.github/FUNDING.yml
vendored
@ -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://yoomoney.ru/to/41001685237530']
|
||||
github: anthonyaxenov
|
||||
patreon: anthonyaxenov
|
||||
custom: [ 'https://yoomoney.ru/to/41001685237530' ]
|
||||
|
49
.github/workflows/ci.yml
vendored
Normal file
49
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
# https://github.com/shivammathur/setup-php/blob/master/README.md
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
Tests:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- 8.0
|
||||
- 8.1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.composer/cache
|
||||
key: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-
|
||||
|
||||
- name: Setup PHP v ${{ matrix.php-version }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions: json, curl, tokenizer, mbstring, xdebug
|
||||
coverage: xdebug
|
||||
tools: composer, phpunit
|
||||
|
||||
- name: Install dependencies with composer
|
||||
run: composer install --no-ansi --no-interaction --no-progress
|
||||
|
||||
- name: Run tests with phpunit/phpunit
|
||||
run: vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml
|
||||
|
||||
- name: Send code coverage report to Codecov.io
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
run: bash <(curl -s https://codecov.io/bash) || true
|
25
.github/workflows/dev.yml
vendored
25
.github/workflows/dev.yml
vendored
@ -1,25 +0,0 @@
|
||||
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: 7.4
|
||||
only_args: --prefer-dist --no-progress
|
||||
|
||||
- name: Run phpunit tests
|
||||
uses: php-actions/phpunit@v8
|
||||
with:
|
||||
configuration: ./phpunit.xml
|
25
.github/workflows/master.yml
vendored
25
.github/workflows/master.yml
vendored
@ -1,25 +0,0 @@
|
||||
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
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@
|
||||
/config.php
|
||||
*cache*
|
||||
/test.php
|
||||
/.coverage-report
|
||||
|
120
README.md
120
README.md
@ -1,115 +1,91 @@
|
||||
# АТОЛ Онлайн
|
||||
|
||||
[![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)
|
||||
[![Latest Stable Version](http://poser.pugx.org/axenov/atol-online/v)](https://packagist.org/packages/axenov/atol-online)
|
||||
[![Latest Unstable Version](http://poser.pugx.org/axenov/atol-online/v/unstable)](https://packagist.org/packages/axenov/atol-online)
|
||||
[![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/axenov/atol-online)](https://packagist.org/packages/axenov/atol-online)
|
||||
[![Total Downloads](https://img.shields.io/packagist/dt/axenov/atol-online)](https://packagist.org/packages/axenov/atol-online)
|
||||
[![License](https://img.shields.io/packagist/l/axenov/atol-online?color=%23369883)](LICENSE)
|
||||
|
||||
Библиотека для фискализации чеков по 54-ФЗ через [облачную ККТ АТОЛ](https://online.atol.ru/).
|
||||
|
||||
Текущая поддерживаемая версия API: **5.1**
|
||||
Библиотека для фискализации чеков по 54-ФЗ через [облачные ККТ АТОЛ](https://online.atol.ru/).
|
||||
|
||||
**[Документация](/docs/readme.md)**
|
||||
|
||||
Текущие поддерживаемые версии АТОЛ Онлайн:
|
||||
|
||||
| Протокол | API | ФФД | Статус |
|
||||
|----------|-----|------|----------------|
|
||||
| v4 | 5.8 | 1.05 | Поддерживается |
|
||||
| v5 | 2.0 | 1.2 | В планах |
|
||||
|
||||
Состояние веток:
|
||||
|
||||
| master | [![GitHub Workflow Status (master)](https://img.shields.io/github/workflow/status/anthonyaxenov/atol-online/CI/master?logo=github)](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml) | [![codecov](https://codecov.io/gh/anthonyaxenov/atol-online/branch/master/graph/badge.svg?token=WR2IV7FTF0)](https://codecov.io/gh/anthonyaxenov/atol-online) |
|
||||
|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| dev | [![GitHub Workflow Status (dev)](https://img.shields.io/github/workflow/status/anthonyaxenov/atol-online/CI/dev?logo=github)](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml) | [![codecov dev](https://codecov.io/gh/anthonyaxenov/atol-online/branch/dev/graph/badge.svg?token=WR2IV7FTF0)](https://codecov.io/gh/anthonyaxenov/atol-online) |
|
||||
|
||||
## Плюшечки
|
||||
|
||||
* Мониторинг ККТ и ФН
|
||||
* Фискализация докумнетов на облачной ККТ
|
||||
* Валидация данных до отправки документа на ККТ (насколько это возможно, согласно схеме)
|
||||
* Расчёты денег в копейках
|
||||
* PSR-4 автозагрузка, покрытие настоящими тестами, fluent-setters
|
||||
|
||||
## Системные требования
|
||||
|
||||
* PHP 7.4+
|
||||
* composer
|
||||
* php-json
|
||||
* php8.0+
|
||||
* [composer](https://getcomposer.org/)
|
||||
* расширения php (скорее всего, устанавливать их отдельно не придётся):
|
||||
* `php-json`
|
||||
* `php-curl`
|
||||
* `php-mbstring`
|
||||
* `php-tokenizer`
|
||||
|
||||
## Начало работы
|
||||
|
||||
### Подключение библиотеки
|
||||
|
||||
1. Подключить пакет к вашему проекту:
|
||||
1. Подключить пакет к проекту:
|
||||
```bash
|
||||
composer require axenov/atol-online
|
||||
```
|
||||
2. В нужном месте проекта объявить параметры, используя константы, и подключить **composer**, если это не сделано ранее:
|
||||
2. В нужном месте проекта подключить автозагрузчик composer-зависимостей, если это не сделано ранее:
|
||||
```php
|
||||
require($project_root.'/vendor/autoload.php');
|
||||
require($project_root . '/vendor/autoload.php');
|
||||
```
|
||||
где `$project_root` — абсолютный путь к корневой директории вашего проекта.
|
||||
> При использовании фреймворков это обычно не требуется.
|
||||
|
||||
### Тестирование кода библиотеки
|
||||
|
||||
Файлы тестов находятся в директории `/tests` корня репозитория.
|
||||
|
||||
Для запуска тестов необходимо перейти в корень вашего проекта и выполнить команду:
|
||||
Для запуска тестов необходимо перейти в корень репозитория и выполнить одну из команд:
|
||||
|
||||
```bash
|
||||
./vendor/bin/phpunit
|
||||
composer test # обычное тестирование
|
||||
composer coverage # тестирование с покрытием
|
||||
```
|
||||
|
||||
## Настройка ККТ
|
||||
|
||||
Для работы с облачной ККТ необходимы следующие параметры:
|
||||
* логин;
|
||||
* пароль;
|
||||
* код группы.
|
||||
|
||||
Чтоб получить их, нужно:
|
||||
1. авторизоваться на [online.atol.ru](https://online.atol.ru/lk/Account/Login);
|
||||
2. на странице [Мои компании](https://online.atol.ru/lk/Company/List) нажать кнопку **Настройки интегратора**.
|
||||
Скачается XML-файл с нужными настройками.
|
||||
|
||||
Также для работы потребуются:
|
||||
* ИНН продавца;
|
||||
* URL места расчёта (ссылка на ваш интернет-сервис).
|
||||
После тестирования с покрытием создаётся отчёт в директории `.coverage` в корне репозитория.
|
||||
|
||||
## Использование библиотеки
|
||||
|
||||
### Доступные методы и классы
|
||||
|
||||
Весь исходный код находится в директории [`/src`](/src).
|
||||
|
||||
**Комментарии phpDoc есть буквально к каждому классу, методу или полю.
|
||||
Прокомментировано вообще всё.**
|
||||
**Комментарии phpdoc есть буквально везде. Прокомментировано вообще всё.**
|
||||
|
||||
1. Обращайтесь к [документации](/docs).
|
||||
1. Обращайтесь к [документации библиотеки](/docs).
|
||||
2. Обращайтесь к [исходному коду](/src).
|
||||
3. Обращайтесь к [тестам](/test).
|
||||
4. **Используйте подсказки вашей IDE.**
|
||||
3. Обращайтесь к [тестам](/tests).
|
||||
4. Используйте подсказки вашей IDE.
|
||||
|
||||
Тогда у вас не возникнет затруднений.
|
||||
|
||||
Для тех, кто решил подробно разобраться в работе библиотеки, отдельно отмечу нюансы, которые могут ускользнуть от внимания:
|
||||
1. Класс `AtolOnline\Api\Kkt` унаследован от `GuzzleHttp\Client` со всеми вытекающими;
|
||||
2. Все классы, унаследованные от `AtolOnline\Entities\AtolEntity` приводятся к JSON-строке.
|
||||
|
||||
### Общий алгоритм
|
||||
|
||||
1. Задать настройки ККТ
|
||||
2. Собрать данные о покупателе
|
||||
3. Собрать данные о продавце
|
||||
4. Собрать данные о предметах расчёта (товары, услуги и пр.)
|
||||
5. Создать документ, добавив в него покупателя, продавца и предметы расчёта
|
||||
6. Отправить документ на регистрацию:
|
||||
6.1. *Необязательно:* задать `callback_url`, на который АТОЛ отправит HTTP POST о состоянии документа.
|
||||
7. Запомнить `uuid` из пришедшего ответа, поскольку он пригодится для последующей проверки статуса фискализации.
|
||||
> Если с документом был передан `callback_url`, то ответ придёт на этот самый URL.
|
||||
Если с документом **НЕ** был передан `callback_url` **либо** callback от АТОЛа не пришёл в течение 300 секунд (5 минут), нужно запрашивать вручную по `uuid`, пришедшему от АТОЛа в ответ на регистрацию документа.
|
||||
8. Проверить состояние документа (нет необходимости, если передавался `callback_url`):
|
||||
8.1. взять `uuid` ответа, полученного на запрос фискализации;
|
||||
8.2. отправить его в запросе состояния документа.
|
||||
> Данные о документе можно получить только в течение 32 суток после успешной фискализации.
|
||||
|
||||
## Об отправке электронного чека покупателю
|
||||
|
||||
После успешной фискализации документа покупатель автоматически получит уведомление **от ОФД**, который используется в связке с вашей ККТ:
|
||||
* **по email**, если в документе указан email клиента;
|
||||
* **по смс**:
|
||||
* если в документе указан номер телефона клиента;
|
||||
* если на стороне ОФД необходима и подключена услуга СМС-информирования (уточняйте подробности о своего ОФД).
|
||||
|
||||
> Если заданы email и телефон, то отдаётся приоритет email.
|
||||
|
||||
## Дополнительные ресурсы
|
||||
|
||||
Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md)
|
||||
|
||||
Официальные ресурсы АТОЛ Онлайн:
|
||||
* **[Вся документация](https://online.atol.ru/lib/)**
|
||||
* Telegram-канал: [@atolonline](https://t.me/atolonline)
|
||||
* [Документация АТОЛ Онлайн](https://online.atol.ru/lib/)
|
||||
|
||||
## Лицензия
|
||||
|
||||
Вы имеете право использовать код из этого репозитория только на условиях **[лицензии MIT](LICENSE)**.
|
||||
Вы имеете право использовать и распространят код из этого репозитория на условиях **[лицензии MIT](LICENSE)**.
|
||||
|
66
ROADMAP.md
66
ROADMAP.md
@ -1,66 +0,0 @@
|
||||
# Roadmap
|
||||
|
||||
Здесь перечислены реализованные функции и находящиеся в разработке.
|
||||
|
||||
Порядок их упоминания здесь может не совпадать с порядком реализации.
|
||||
|
||||
Эталонная реализация подразумевает полную поддержку всех методов API и обеих схем документов:
|
||||
* [Документы прихода, возврата прихода, расхода, возврата расхода](https://online.atol.ru/possystem/v4/schema/sell)
|
||||
* [Документы коррекции прихода, коррекции расхода](https://online.atol.ru/possystem/v4/schema/correction)
|
||||
|
||||
## Общий функционал библиотеки
|
||||
|
||||
- [x] Переключение настроек доступа к ККТ при переключении тестового режима
|
||||
- [x] Тесты для класса налоговой ставки (+ массив)
|
||||
- [ ] Тесты для класса оплаты (+ массив)
|
||||
- [x] Тесты для класса предмета расчёта (+ массив)
|
||||
- [x] Тесты для класса клиента
|
||||
- [x] Тесты для класса компании
|
||||
- [ ] Тесты для класса данных коррекций
|
||||
- [ ] Тесты для класса документа
|
||||
- [ ] Тесты для класса ответа ККТ
|
||||
- [ ] Тесты для регистрации документа прихода
|
||||
- [ ] Тесты для регистрации документа возврата прихода
|
||||
- [ ] Тесты для регистрации документа коррекции прихода
|
||||
- [ ] Тесты для регистрации документа расхода
|
||||
- [ ] Тесты для регистрации документа возврата расхода
|
||||
- [ ] Тесты для регистрации документа коррекции расхода
|
||||
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках.
|
||||
Рубли (дроби) должны быть только в JSON-представлениях
|
||||
|
||||
## Поддержка методов API (регистрация документов)
|
||||
|
||||
- [x] приход
|
||||
- [x] расход
|
||||
- [x] возврат прихода
|
||||
- [x] возврат расхода
|
||||
- [x] коррекция прихода
|
||||
- [x] коррекция расхода
|
||||
- [x] проверка статуса документа
|
||||
|
||||
## Документы прихода, возврата прихода, расхода, возврата расхода
|
||||
|
||||
- [x] Пoддержка `receipt.client` (обязательный)
|
||||
- [x] Пoддержка `receipt.company` (обязательный)
|
||||
- [x] Пoддержка `receipt.items` (обязательный)
|
||||
- [x] Пoддержка `receipt.total` (обязательный)
|
||||
- [x] Пoддержка `receipt.payments` (обязательный)
|
||||
- [x] Пoддержка `receipt.vats`
|
||||
- [ ] Пoддержка `receipt.additional_check_props`
|
||||
- [x] Пoддержка `receipt.cashier`
|
||||
- [ ] Пoддержка `receipt.additional_user_props`
|
||||
- [ ] Пoддержка `receipt.agent_info`
|
||||
- [ ] Пoддержка `receipt.supplier_info`
|
||||
- [ ] Пoддержка `receipt.items.agent_info`
|
||||
- [ ] Пoддержка `receipt.items.supplier_info`
|
||||
- [ ] Пoддержка `receipt.items.nomenclature_code`
|
||||
- [ ] Пoддержка `receipt.items.excise`
|
||||
- [ ] Пoддержка `receipt.items.country_code`
|
||||
- [ ] Пoддержка `receipt.items.declaration_number`
|
||||
|
||||
## Документы коррекции прихода, коррекции расхода
|
||||
|
||||
- [x] Пoддержка `correction.company` (обязательный)
|
||||
- [x] Пoддержка `correction.vats` (обязательный)
|
||||
- [x] Пoддержка `correction.correction_info` (обязательный)
|
||||
- [x] Пoддержка `correction.cashier`
|
@ -1,22 +1,26 @@
|
||||
{
|
||||
"name": "axenov/atol-online",
|
||||
"description": "Library to use cloud cash register in e-commerce according to Russian Federal Law #54",
|
||||
"description": "Библиотека для работы с API АТОЛ Онлайн (облачные ККТ для приёма платежей по 54-ФЗ)",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"api",
|
||||
"e-commerce",
|
||||
"54-fz",
|
||||
"54-фз",
|
||||
"kkt",
|
||||
"ккт",
|
||||
"e-commerce",
|
||||
"cash",
|
||||
"cash register",
|
||||
"payment",
|
||||
"payment system",
|
||||
"atol",
|
||||
"атол",
|
||||
"atol-online",
|
||||
"атол онлайн"
|
||||
"atol online",
|
||||
"атол онлайн",
|
||||
"fiscalization",
|
||||
"фискализация",
|
||||
"чек"
|
||||
],
|
||||
"homepage": "https://github.com/anthonyaxenov/atol-online",
|
||||
"readme": "https://github.com/anthonyaxenov/atol-online/blob/master/README.md",
|
||||
@ -28,37 +32,52 @@
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"rss": "https://github.com/anthonyaxenov/atol-online/discussions/categories/announcements",
|
||||
"chat": "https://github.com/anthonyaxenov/atol-online/discussions",
|
||||
"source": "https://github.com/anthonyaxenov/atol-online",
|
||||
"issues": "https://github.com/anthonyaxenov/atol-online/issues",
|
||||
"docs": "https://github.com/anthonyaxenov/atol-online/blob/master/docs/readme.md"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "Patreon",
|
||||
"url": "https://www.patreon.com/anthonyaxenov"
|
||||
},
|
||||
{
|
||||
"type": "Yoomoney",
|
||||
"url": "https://yoomoney.ru/to/41001685237530"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"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",
|
||||
"jetbrains/phpstorm-attributes": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5"
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"AtolOnline\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"src/AtolOnline/Api/",
|
||||
"src/AtolOnline/Exceptions/",
|
||||
"src/AtolOnline/Entities/",
|
||||
"src/AtolOnline/Traits/",
|
||||
"src/AtolOnline/Constants/",
|
||||
"tests/"
|
||||
],
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit --colors=always",
|
||||
"coverage": "php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html .coverage"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
|
2043
composer.lock
generated
2043
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,83 +0,0 @@
|
||||
# Работа с клиентами (покупателями)
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
Объект покупателя инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$customer = new AtolOnline\Entities\Client();
|
||||
```
|
||||
|
||||
У объекта покупателя могут быть указаны любые из следующих атрибутов:
|
||||
* email (тег ФФД 1008);
|
||||
* ИНН (тег ФФД 1128);
|
||||
* наименование (тег ФФД 1127);
|
||||
* номер телефона (тег ФФД 1008).
|
||||
|
||||
> Все эти атрибуты являются **необязательными**.
|
||||
> Если указаны одновременно и email, и номер телефона, то ОФД отправит чек только на email.
|
||||
|
||||
Указать эти атрибуты можно двумя способами:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$customer = new AtolOnline\Entities\Client(
|
||||
'John Doe', // наименование
|
||||
'+1/22/99*73s dsdas654 5s6', // номер телефона +122997365456
|
||||
'john@example.com', // email
|
||||
'+fasd3\qe3fs_=nac990139928czc' // номер ИНН 3399013928
|
||||
);
|
||||
|
||||
// 2 способ - через сеттеры
|
||||
$customer = (new AtolOnline\Entities\Client())
|
||||
->setEmail('john@example.com')
|
||||
->setInn('+fasd3\q3fs_=nac9901 3928c-c') // 3399013928
|
||||
->setName('John Doe')
|
||||
->setPhone('+1/22/99*73s dsdas654 5s6'); // +122997365456
|
||||
|
||||
// либо комбинация этих способов
|
||||
```
|
||||
|
||||
Метод `setEmail()` проверяет входную строку на длину (до 64 символов) и валидность формата email.
|
||||
Выбрасывает исключения:
|
||||
* `AtolEmailTooLongException` (если слишком длинный email);
|
||||
* `AtolEmailValidateException` (если email невалиден).
|
||||
|
||||
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
|
||||
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
|
||||
|
||||
Метод `setName()` проверяет входную строку на длину (до 256 символов).
|
||||
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное имя).
|
||||
|
||||
Метод `setPhone()` чистит входную строку от всех символов, кроме цифр и знака `+`, и проверяет длину (до 64 символов).
|
||||
Выбрасывает исключение `AtolPhoneTooLongException` (если слишком длинный номер телефона).
|
||||
|
||||
Конструктор может выбрасывать любое из указанных выше исключений, если в него передаются значения.
|
||||
|
||||
Получить установленные значения атрибутов можно через геттеры:
|
||||
|
||||
```php
|
||||
$customer->getInn();
|
||||
$customer->getEmail();
|
||||
$customer->getName();
|
||||
$customer->getPhone();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $customer;
|
||||
$json_string = (string)$customer;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $customer->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
19
docs/collection.md
Normal file
19
docs/collection.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Коллекция сущностей
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
||||
|
||||
---
|
||||
|
||||
Коллекциями являются объекты, способные хранить в себе [сущности](entity.md). Они унаследованы
|
||||
от `Illuminate/Support/Collection` и полностью поддерживают все
|
||||
[стандартные методы коллекций Laravel](https://laravel.com/docs/master/collections).
|
||||
|
||||
Помимо этого, они валидируют количество и вид сущностей, которые могут хранить в себе, согласно схеме АТОЛ Онлайн API.
|
||||
|
||||
Коллекции ведут себя аналогично самим сущностям в части приведения к массивам и json-ификации.
|
||||
|
||||
---
|
||||
|
||||
Читай также: [Сущность](entity.md)
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
@ -1,80 +0,0 @@
|
||||
# Работа с компанией (продавцом)
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
Объект компании инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$customer = new AtolOnline\Entities\Company();
|
||||
```
|
||||
|
||||
У объекта компании должны быть указаны все следующие атрибуты:
|
||||
* email (тег ФФД 1117);
|
||||
* ИНН (тег ФФД 1018);
|
||||
* тип системы налогообложения (тег ФФД 1055) - все типы перечислены в классе `AtolOnline\Constants\SnoTypes`;
|
||||
* адрес места расчётов (тег ФФД 1187) - для интернет-сервисов указывается URL с протоколом.
|
||||
|
||||
> Все эти атрибуты являются **обязательными**.
|
||||
> Для тестового режима используйте значения ИНН и адреса места расчётов, [указанные здесь](https://online.atol.ru/files/ffd/test_sreda.txt).
|
||||
|
||||
Указать эти атрибуты можно двумя способами:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$company = new AtolOnline\Entities\Company(
|
||||
AtolOnline\Constants\SnoTypes::OSN, // тип СНО
|
||||
'5544332219', // номер ИНН
|
||||
'https://v4.online.atol.ru', // адрес места расчётов
|
||||
'company@example.com' // email
|
||||
);
|
||||
|
||||
// 2 способ - через сеттеры
|
||||
$company = (new AtolOnline\Entities\Company())
|
||||
->setEmail('company@example.com')
|
||||
->setInn('5544332219')
|
||||
->setSno(AtolOnline\Constants\SnoTypes::USN_INCOME)
|
||||
->setPaymentAddress('https://v4.online.atol.ru');
|
||||
|
||||
// либо комбинация этих способов
|
||||
```
|
||||
|
||||
Метод `setEmail()` проверяет входную строку на длину (до 64 символов) и валидность формата email.
|
||||
Выбрасывает исключения:
|
||||
* `AtolEmailTooLongException` (если слишком длинный email);
|
||||
* `AtolEmailValidateException` (если email невалиден).
|
||||
|
||||
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
|
||||
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
|
||||
|
||||
Метод `setPaymentAddress()` проверяет длину (до 256 символов).
|
||||
Выбрасывает исключение `AtolPaymentAddressTooLongException` (если слишком длинный адрес места расчётов).
|
||||
|
||||
Конструктор может выбрасывать любое из указанных выше исключений, если в него передаются параметры.
|
||||
|
||||
Получить установленные значения параметров можно через геттеры:
|
||||
|
||||
```php
|
||||
$company->getInn();
|
||||
$company->getEmail();
|
||||
$company->getPaymentAddress();
|
||||
$company->getSno();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $company;
|
||||
$json_string = (string)$company;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $company->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
@ -1,66 +0,0 @@
|
||||
# Работа с данными коррекции
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
Объект для данных коррекции инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$info = new AtolOnline\Entities\CorrectionInfo();
|
||||
```
|
||||
|
||||
У объекта должны быть указаны все следующие обязательные атрибуты:
|
||||
* тип коррекции (тег ФФД 1173) - все типы перечислены в классе `AtolOnline\Constants\CorrectionTypes`;
|
||||
* дата документа основания для коррекции в формате `d.m.Y` (тег ФФД 1178);
|
||||
* номер документа основания для коррекции (тег ФФД 1179);
|
||||
* описание коррекции (тег ФФД 1177).
|
||||
|
||||
Указать эти атрибуты можно двумя способами:
|
||||
|
||||
```php
|
||||
use AtolOnline\{Entities\CorrectionInfo, Constants\CorrectionTypes};
|
||||
|
||||
// 1 способ - через конструктор
|
||||
$info = new CorrectionInfo(
|
||||
CorrectionTypes::SELF, // тип коррекции
|
||||
'01.01.2019', // дата документа коррекции
|
||||
'12345', // номер документа коррекции
|
||||
'test' // описание коррекции
|
||||
);
|
||||
|
||||
// 2 способ - через сеттеры
|
||||
$info = (new CorrectionInfo())
|
||||
->setType(CorrectionTypes::INSTRUCTION)
|
||||
->setDate('01.01.2019')
|
||||
->setName('test')
|
||||
->setNumber('9999');
|
||||
|
||||
// либо комбинация этих способов
|
||||
```
|
||||
|
||||
Получить установленные значения атрибутов можно через геттеры:
|
||||
|
||||
```php
|
||||
$info->getType();
|
||||
$info->getDate();
|
||||
$info->getName();
|
||||
$info->getNumber();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $customer;
|
||||
$json_string = (string)$customer;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $customer->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
@ -1,167 +0,0 @@
|
||||
# Работа с документами
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
Объект документа инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$doc = new AtolOnline\Entities\Document();
|
||||
```
|
||||
|
||||
Для документов **прихода, возврата прихода, расхода и возврата расхода** должны быть указаны все следующие обязательные атрибуты:
|
||||
* [клиент](/docs/client.md);
|
||||
* [компания](/docs/company.md);
|
||||
* [предметы расчёта](/docs/items.md);
|
||||
* [оплаты](/docs/payments.md).
|
||||
|
||||
Для документов **коррекции прихода и коррекции расхода** должны быть указаны все следующие обязательные атрибуты:
|
||||
* [компания](/docs/company.md);
|
||||
* [оплаты](/docs/payments.md);
|
||||
* [ставки НДС](/docs/vats.md);
|
||||
* [данные коррекции](/docs/correction_info.md).
|
||||
|
||||
Для любых документов также могут быть указаны следующие необязательные атрибуты:
|
||||
* ФИО кассира (тег ФФД - 1021).
|
||||
|
||||
Установка атрибутов документа происходит через сеттеры.
|
||||
|
||||
## Работа с клиентом
|
||||
|
||||
Для этого существуют следующие методы:
|
||||
|
||||
```php
|
||||
$doc->setClient($client);
|
||||
$doc->getClient();
|
||||
```
|
||||
|
||||
> О работе с клиентами более подробно читайте [здесь](/docs/client.md).
|
||||
|
||||
## Работа с компанией
|
||||
|
||||
Для этого существуют следующие методы:
|
||||
|
||||
```php
|
||||
$doc->setCompany($company);
|
||||
$doc->getCompany();
|
||||
```
|
||||
|
||||
> О работе с компаниями более подробно читайте [здесь](/docs/company.md).
|
||||
|
||||
## Работа с предметами расчёта
|
||||
|
||||
Внутри документа существует [массив предметов расчёта](/docs/items.md#array).
|
||||
По умолчанию он пуст.
|
||||
Напрямую для манипуляций объект массива недоступен.
|
||||
Работа с ним происходит через методы документа:
|
||||
|
||||
```php
|
||||
$doc->setItems([$item1, $item2]);
|
||||
$doc->addItem($item3);
|
||||
$doc->getItems();
|
||||
```
|
||||
|
||||
Соответственно, эти методы выбрасывают те же исключения, что методы самого массива.
|
||||
|
||||
## Работа с оплатами
|
||||
|
||||
Внутри документа существует [массив оплат](/docs/payments.md#array).
|
||||
По умолчанию он пуст.
|
||||
Напрямую для манипуляций объект массива недоступен.
|
||||
Работа с ним происходит через методы документа:
|
||||
|
||||
```php
|
||||
$doc->setPayments([$payment1, $payment2]);
|
||||
$doc->addPayment($payment3);
|
||||
$doc->getPayments();
|
||||
```
|
||||
|
||||
Соответственно, эти методы выбрасывают те же исключения, что методы самого массива.
|
||||
|
||||
Следует отметить, что если при выполнении метода `addPayment()` выполняются следующие условия:
|
||||
* аргументом передан объект оплаты, у которого не задана сумма,
|
||||
* ранее документу не задавались оплаты,
|
||||
то автоматически этому объекту оплаты задаётся полная сумма чека.
|
||||
|
||||
## Работа со ставками НДС
|
||||
|
||||
Внутри документа существует [массив ставок НДС](/docs/vats.md#array).
|
||||
По умолчанию он пуст.
|
||||
Напрямую для манипуляций объект массива недоступен.
|
||||
Работа с ним происходит через методы документа:
|
||||
|
||||
```php
|
||||
$doc->setVats([$vat1, $vat2]);
|
||||
$doc->addVat($vat3);
|
||||
$doc->getVats();
|
||||
```
|
||||
|
||||
Соответственно, эти методы выбрасывают те же исключения, что методы самого массива.
|
||||
|
||||
Также существует метод `clearVats()`, который удаляет все вложенные объекты ставок НДС - из предметов расчёта и самого документа.
|
||||
|
||||
Следует отметить, что если при выполнении метода `addVat()` выполняются следующие условия:
|
||||
* аргументом передан объект ставки, у которого не задана сумма,
|
||||
* ранее документу не задавались ставки,
|
||||
то автоматически этому объекту налога задаётся полная сумма чека, от которой расчитывается итоговый размер налога.
|
||||
|
||||
## Общая сумма документа
|
||||
|
||||
Расчёт происходит автоматически в следующих случаях:
|
||||
* изменение предметов расчёта (`setItems()`, `addItem()`);
|
||||
* добавление оплат (`addPayment()` в случае, когда оплата передана без суммы);
|
||||
* изменение ставок НДС (`setVats()`, `clearVats()`, `addVat()` в случае, когда ставка передана без суммы);
|
||||
* приведение объекта документа к строке.
|
||||
|
||||
Также можно вызвать вручную метод `calcTotal()`.
|
||||
Он расчитывает полную сумму чека по предметам расчёта и пересчитывает **все** налоговые ставки.
|
||||
|
||||
Получить итог можно с помощью метода `getTotal()`.
|
||||
|
||||
Всё в рублях.
|
||||
|
||||
<a name='correction'></a>
|
||||
## Работа с данными коррекции
|
||||
|
||||
Если документ создаётся с целью коррекции прихода или расхода, то он обязательно должен содержать [данные коррекции](/docs/correction_info.md).
|
||||
|
||||
Задать и получить эти данные очень просто:
|
||||
|
||||
```php
|
||||
$doc->setCorrectionInfo(new AtolOnline\Entities\CorrectionInfo(
|
||||
AtolOnline\Constants\CorrectionTypes::SELF, // тип коррекции
|
||||
'01.01.2019', // дата документа коррекции
|
||||
'12345', // номер документа коррекции
|
||||
'test' // описание коррекции
|
||||
));
|
||||
$doc->getCorrectionInfo();
|
||||
```
|
||||
|
||||
## Работа с кассиром
|
||||
|
||||
Для этого существуют следующие методы:
|
||||
|
||||
```php
|
||||
$doc->setCashier('Иванова Лариса Васильевна');
|
||||
$doc->getCashier();
|
||||
```
|
||||
|
||||
## Прочее
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $doc;
|
||||
$json_string = (string)$doc;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $doc->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
52
docs/entity.md
Normal file
52
docs/entity.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Сущность
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
||||
|
||||
---
|
||||
|
||||
Сущностями являются все классы, которые необходимы для взаимодействия с API. Они находятся в директори `src/Entities` и
|
||||
расширяют абстрактный класс `AtolOnline\Entities\Entity`.
|
||||
|
||||
Каждая сущность содержит в себе только те данные, которые необходимы согласно схемы АТОЛ Онлайн API.
|
||||
|
||||
Ниже перечислены возможности сущностей.
|
||||
|
||||
## Приведение к строке JSON
|
||||
|
||||
```php
|
||||
echo $entity;
|
||||
$json_string = (string)$entity;
|
||||
```
|
||||
|
||||
## Приведение к массиву
|
||||
|
||||
```php
|
||||
// результат идентичен
|
||||
$json_array1 = $entity->jsonSerialize();
|
||||
$json_array2 = $entity->toArray();
|
||||
```
|
||||
|
||||
## Чтение из массива
|
||||
|
||||
```php
|
||||
$var = new \AtolOnline\Entities\Client('Иванов Иван');
|
||||
echo $var['name']; // 'Иванов Иван'
|
||||
$var['name'] = 'Петров Пётр'; // BadMethodCallException
|
||||
```
|
||||
|
||||
## Fluent-сеттеры
|
||||
|
||||
Реализованы на уровне конкретных классов сущностей, но у некоторых могут полностью отсуствовать.
|
||||
|
||||
```php
|
||||
$entity->setFoo($value)->setBar('bar')->...
|
||||
```
|
||||
|
||||
Сеттеры валидируют и фильтруют данные согласно схеме АТОЛ Онлайн API, а в случае ошибочных значений -- выбрасывают
|
||||
исключения.
|
||||
|
||||
---
|
||||
|
||||
Читай также: [Коллекция сущностей](collection.md)
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
237
docs/fiscalizing.md
Normal file
237
docs/fiscalizing.md
Normal file
@ -0,0 +1,237 @@
|
||||
# Фискализация документов
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
||||
|
||||
---
|
||||
|
||||
## Доступ к ККТ
|
||||
|
||||
Для работы с облачной ККТ необходимы следующие параметры:
|
||||
|
||||
* логин;
|
||||
* пароль;
|
||||
* код группы.
|
||||
|
||||
Чтоы получить их, нужно:
|
||||
|
||||
1. авторизоваться в личном кабинете [online.atol.ru](https://online.atol.ru/lk/Account/Login);
|
||||
2. на странице [Мои компании](https://online.atol.ru/lk/Company/List) нажать кнопку **Настройки интегратора**.
|
||||
Скачается XML-файл с нужными настройками.
|
||||
|
||||
Также для работы потребуются:
|
||||
|
||||
* ИНН продавца;
|
||||
* URL места расчёта (ссылка на ваш интернет-сервис).
|
||||
|
||||
## Использование
|
||||
|
||||
Объект ККТ инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$kkt = new AtolOnline\Api\Fiscalizer();
|
||||
```
|
||||
|
||||
Установить параметры подключения можно двумя путями:
|
||||
|
||||
```php
|
||||
use AtolOnline\Api\Fiscalizer;
|
||||
|
||||
// 1 способ - через конструктор
|
||||
$kkt = new Fiscalizer(group: 'mygroup', login: 'mylogin', password: 'mypassword');
|
||||
|
||||
// 2 способ - через сеттеры
|
||||
$kkt = (new Fiscalizer())
|
||||
->setLogin($login)
|
||||
->setGroup($group)
|
||||
->setPassword($password);
|
||||
```
|
||||
|
||||
<a name="testmode"></a>
|
||||
|
||||
## Тестовый режим
|
||||
|
||||
По умолчанию фискализатор создаётся для работы в тестовом режиме. Это означает, что работа с АТОЛ Онлайн API будет
|
||||
происходить [в тестовой среде](https://online.atol.ru/files/ffd/test_sreda.txt).
|
||||
|
||||
> Под тестовым режимом работы подразумевается использование тестовых ККТ, которые принадлежат компании АТОЛ.
|
||||
|
||||
Управление тестовым режимом происходит следующим образом:
|
||||
|
||||
```php
|
||||
$kkt = new Fiscalizer(); // включен по умолчанию
|
||||
$kkt = new Fiscalizer(false); // выключен явно
|
||||
$kkt->setTestMode(); // включен явно
|
||||
$kkt->setTestMode(true); // включен явно
|
||||
$kkt->setTestMode(false); // выключен явно
|
||||
```
|
||||
|
||||
**При включенном тестовом режиме используются тестовые ККТ**, т.к. перед отправкой запроса подменяются:
|
||||
|
||||
* логин;
|
||||
* пароль;
|
||||
* группа ККТ;
|
||||
* ИНН клиента (покупателя);
|
||||
* ИНН и адрес места расчётов компании (продавца).
|
||||
|
||||
Таким образом:
|
||||
|
||||
* использовать тестовый режим -- безопасно;
|
||||
* при переключении тестового режима устанавливать заново свои параметры подключения не требуется.
|
||||
|
||||
**При выключенном тестовом режиме используются ваши ККТ.**
|
||||
|
||||
Если по каким-то причинам у вас не получится использовать тестовый режим, вы можете проводить свои тесты в боевом
|
||||
режиме (на собственной ККТ). В этом случае важно понимать следующее:
|
||||
|
||||
1. сразу после оформления документа **прихода** необходимо оформлять точно такой же документ **возврата прихода**;
|
||||
2. [вы обязательно забудете о пункте 1](http://murphy-law.net.ru/basics.html);
|
||||
3. пп. 1 и 2 в любом случае скажутся на ваших финансовых отчётах;
|
||||
4. вся ответственность за пп. 1-3 и последствия ложится только на вас.
|
||||
|
||||
## Авторизация на ККТ
|
||||
|
||||
Перед первым запросом на ККТ происходит аутентификация на сервере по логину и паролю. В ответ приходит авторизационный
|
||||
токен, срок жизни коего равен **24 часам**. После первой успешной операции возможно получить этот токен следующим
|
||||
образом:
|
||||
|
||||
```php
|
||||
$kkt->getToken(); // вернёт строку длиной 128 символа
|
||||
```
|
||||
|
||||
Этот токен можно сохранить и переиспользовать в течение всего срока его жизни, но далее следует получить новый токен.
|
||||
|
||||
Ранее полученный токен следует указывать до отправки запросов следующим образом:
|
||||
|
||||
```php
|
||||
$kkt->setToken($token_string);
|
||||
```
|
||||
|
||||
Если токен был установлен перед выполнением операции, то при выполнении операции будет использоваться именно он. Если
|
||||
операция завершится ошибочно из-за истёкшего токена, следует повторить операцию без использования метода `setToken()`,
|
||||
либо обнулив его следующим образом:
|
||||
|
||||
```php
|
||||
$kkt->setToken(null);
|
||||
```
|
||||
|
||||
Тогда будет получен новый токен.
|
||||
|
||||
## Регистрация документа
|
||||
|
||||
Для регистрации документа **прихода** необходимо вызвать метод `sell()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->sell($document);
|
||||
$result2 = $receipt->sell($kkt);
|
||||
```
|
||||
|
||||
Для регистрации документа **возврата прихода** необходимо вызвать метод `sellRefund()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->sellRefund($document);
|
||||
$result2 = $receipt->sellRefund($kkt);
|
||||
```
|
||||
|
||||
Для регистрации документа **расхода** необходимо вызвать метод `buy()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->buy($document);
|
||||
$result2 = $receipt->buy($kkt);
|
||||
```
|
||||
|
||||
Для регистрации документа **возврата расхода** необходимо вызвать метод `buyRefund()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->buyRefund($document);
|
||||
$result2 = $receipt->buyRefund($kkt);
|
||||
```
|
||||
|
||||
Для регистрации документа **коррекции прихода** необходимо вызвать метод `sellCorrection()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->sellCorrect($document);
|
||||
$result2 = $correction->sellCorrect($kkt);
|
||||
```
|
||||
|
||||
Для регистрации документа **коррекции расхода** необходимо вызвать метод `buyCorrection()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->buyCorrect($document);
|
||||
$result2 = $correction->buyCorrect($kkt);
|
||||
```
|
||||
|
||||
### Собственный идентификатор документа (`external_id`)
|
||||
|
||||
Каждый документ, переданный на ККТ для регистрации, всегда имеет свой идентификатор, абсолютно уникальный среди всех
|
||||
документов когда-либо регистрировавшихся на ККТ, даже если при регистрации были ошибки. По умолчанию это UUID версии 4.
|
||||
|
||||
Чтобы использовать собственный идентификатор, следует передать нужное строковое значение вторым параметром в любой из
|
||||
шести описанных выше методов, например:
|
||||
|
||||
```php
|
||||
$result = $kkt->sellRefund($document, 'order_' . $order->id);
|
||||
```
|
||||
|
||||
Если `external_id` не указан явно или имеет пустое значение, то будет сгенерирован новый UUID. Узнать его можно будет
|
||||
только в ответе от ККТ после регистрации документа в очереди на фискализацию.
|
||||
|
||||
### Передача `callback_url`
|
||||
|
||||
Перед регистрацией документа можно указать `callback_url`. АТОЛ отправит на указанный URL результат регистрации. По
|
||||
этому адресу должен располагаться ваш собственный обработчик статуса фискализации.
|
||||
|
||||
```php
|
||||
$kkt->setCallbackUrl('http://example.com/process-kkt-result');
|
||||
$kkt->getCallbackUrl();
|
||||
```
|
||||
|
||||
## Проверка статуса документа
|
||||
|
||||
Если перед отправкой документа на регистрацию был задан `callback_url` через метод `setCallbackUrl()`, то ответ придёт
|
||||
на указанный адрес автоматически, как только документ обработается на стороне ККТ. Ответ может быть как об успешной
|
||||
регистрации, так и ошибочной.
|
||||
|
||||
В любом случае, вам доступны два метода, с помощью которых вы можете проверять статус документа самостоятельно:
|
||||
|
||||
```php
|
||||
$kkt->getDocumentStatus(); // делает единичный запрос
|
||||
$kkt->pollDocumentStatus(); // делает запросы до получения конечного статуса (не-wait)
|
||||
```
|
||||
|
||||
Эти методы принимают на вход `uuid` кода регистрации. Этот UUID нужно взять из ответа, полученного при отправке
|
||||
документа на регистрацию:
|
||||
|
||||
```php
|
||||
$sell_result = $kkt->sell($document);
|
||||
$status = $kkt->pollDocumentStatus($sell_result->uuid);
|
||||
```
|
||||
|
||||
Метод `pollDocumentStatus()` многократно опрашивает ККТ на предмет состояния документа. Метод может принимать до трёх
|
||||
параметров:
|
||||
|
||||
* uuid;
|
||||
* количество попыток (по умолчанию — 5);
|
||||
* время между попытками в секундах (по умолчанию — 1).
|
||||
|
||||
```php
|
||||
// Проверять статус 10 раз на протяжении 20 секунд — каждые две секунды
|
||||
$kkt->pollDocumentStatus($sell_result->uuid, 10, 20);
|
||||
```
|
||||
|
||||
Учитывайте, что метод вернёт результат как только сменится статус регистрации на успешный `done` или ошибочный `error`.
|
||||
|
||||
Использовать его лучше сразу после отправки документа на регистрацию (как в примере выше).
|
||||
|
||||
> Как правило, фискализация одного документа занимает 4-6 секунд с учётом регистрации.
|
||||
|
||||
Метод `getDocumentStatus()` принимает на вход только `uuid` и запрашивает состояние документа лишь единожды.
|
||||
Использовать его целесообразнее в те моменты, когда нет необходимости знать успех регистрации сразу после отправки
|
||||
документа.
|
||||
|
||||
> Обратите внимание, что АТОЛ позволяет получать статус документа в течение 32 суток с момента его регистрации.
|
||||
|
||||
---
|
||||
|
||||
Читай также: [Обработка ответа API](response.md)
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
202
docs/items.md
202
docs/items.md
@ -1,202 +0,0 @@
|
||||
# Работа с предметами расчёта
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
## Один объект
|
||||
|
||||
Объект предмета расчёта инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$vat = new AtolOnline\Entities\Item();
|
||||
```
|
||||
|
||||
У объекта предмета расчёта должны быть указаны все следующие обязательные атрибуты:
|
||||
* наименование (тег ФФД - 1030);
|
||||
* цена (тег ФФД - 1079);
|
||||
* количество, вес (тег ФФД - 1023).
|
||||
|
||||
У объекта предмета расчёта также могут быть указаны следующие необязательные атрибуты:
|
||||
* единица измерения количества (тег ФФД - 1197);
|
||||
* признак способа оплаты (тег ФФД - 1214) - перечислены в классе `AtolOnline\Constants\PaymentMethods`;
|
||||
* признак предмета расчёта (тег ФФД - 1212) - перечислены в классе `AtolOnline\Constants\PaymentObjects`;
|
||||
* [ставка НДС](/docs/vats.md);
|
||||
* дополнительный реквизит (тег ФФД - 1191).
|
||||
|
||||
Установить многие (но не все) атрибуты можно следующими способами:
|
||||
|
||||
```php
|
||||
use AtolOnline\{
|
||||
Constants\PaymentMethods,
|
||||
Constants\PaymentObjects,
|
||||
Constants\VatTypes,
|
||||
Entities\Item
|
||||
};
|
||||
|
||||
// 1 способ - через конструктор
|
||||
$item = new Item(
|
||||
'Банан', // наименование
|
||||
100, // цена
|
||||
1, // количество, вес
|
||||
'кг', // единица измерения
|
||||
VatTypes::VAT20, // ставка НДС
|
||||
PaymentObjects::SERVICE, // признак предмета расчёта
|
||||
PaymentMethods::FULL_PAYMENT // признак способа расчёта
|
||||
);
|
||||
|
||||
// 2 способ - через сеттеры
|
||||
$item = new Item();
|
||||
$item->setName('Банан');
|
||||
$item->setPrice(100);
|
||||
$item->setQuantity(2.41);
|
||||
//$item->setQuantity(2.41, 'кг');
|
||||
$item->setMeasurementUnit('кг');
|
||||
$item->setVatType(VatTypes::VAT20);
|
||||
$item->setPaymentObject(PaymentObjects::COMMODITY);
|
||||
$item->setPaymentMethod(PaymentMethods::FULL_PAYMENT);
|
||||
```
|
||||
|
||||
Метод `setName()` проверяет входную строку на длину (до 128 символов).
|
||||
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное наименование).
|
||||
|
||||
Метод `setPrice()` проверяет аргумент на величину (до 42949672.95) и пересчитывает общую стоимость.
|
||||
Выбрасывает исключение `AtolPriceTooHighException` (если цена слишком высока).
|
||||
|
||||
Метод `setMeasurementUnit()` проверяет входную строку на длину (до 16 символов).
|
||||
Выбрасывает исключение `AtolUnitTooLongException` (если слишком длинная строка единицы измерения).
|
||||
|
||||
Метод `setQuantity()` проверяет первый аргумент на величину (до 99999.999) и пересчитывает общую стоимость.
|
||||
Выбрасывает исключения:
|
||||
* `AtolQuantityTooHighException` (если количество слишком велико);
|
||||
* `AtolPriceTooHighException` (если общая стоимость слишком велика).
|
||||
|
||||
Также вторым аргументом может принимать единицу измерения количества.
|
||||
В этом случае дополнительно работает сеттер `setMeasurementUnit()`.
|
||||
|
||||
Метод `setVatType()` задаёт тип ставки НДС, пересчитывает размер налога и общую стоимость.
|
||||
Выбрасывает исключение `AtolPriceTooHighException` (если цена слишком высока).
|
||||
Может принимать `null` для удаления налога.
|
||||
|
||||
Дополнительный реквизит устанавливается отдельным методом `setUserData()`:
|
||||
|
||||
```php
|
||||
$item->setUserData('some data');
|
||||
```
|
||||
|
||||
Он проверяет строку на длину (до 64 символов).
|
||||
Выбрасывает исключение `AtolUserdataTooLongException` (если слишком длинный дополнительный реквизит).
|
||||
|
||||
Для установки признака предмета расчёта существует метод `setPaymentObject()`.
|
||||
На вход следует передавать одной из значений, перечисленных в классе `AtolOnline\Constants\PaymentObjects`.
|
||||
|
||||
```php
|
||||
$item->setPaymentObject(AtolOnline\Constants\PaymentObjects::JOB);
|
||||
```
|
||||
|
||||
Для установки признака способа оплаты существует метод `setPaymentMethod()`.
|
||||
На вход следует передавать одной из значений, перечисленных в классе `AtolOnline\Constants\PaymentMethods`.
|
||||
|
||||
```php
|
||||
$item->setPaymentMethod(AtolOnline\Constants\PaymentMethods::FULL_PAYMENT);
|
||||
```
|
||||
|
||||
Для получения заданных значений атрибутов реализованы соответствующие геттеры:
|
||||
|
||||
```php
|
||||
$item->getName();
|
||||
$item->getPrice();
|
||||
$item->getQuantity();
|
||||
$item->getMeasurementUnit();
|
||||
$item->getPaymentMethod();
|
||||
$item->getPaymentObject();
|
||||
$item->getVat(); // возвращает объект ставки НДС либо null
|
||||
$item->getUserData();
|
||||
```
|
||||
|
||||
Для пересчёта общей стоимости и размера налога существует метод `calcSum()`.
|
||||
|
||||
```php
|
||||
$item->calcSum();
|
||||
```
|
||||
|
||||
Этот метод отрабатывает при вызове `setPrice()`, `setQuantity()` и `setVatType()`.
|
||||
Выбрасывает исключение `AtolPriceTooHighException` (если общая сумма слишком высока).
|
||||
|
||||
Получить уже расчитанную общую сумму можно простым геттером:
|
||||
|
||||
```php
|
||||
$item->getSum();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $item;
|
||||
$json_string = (string)$item;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $item->jsonSerialize();
|
||||
```
|
||||
|
||||
<a name="array"></a>
|
||||
## Массив объектов предметов расчёта
|
||||
|
||||
> Максимальное количество объектов в массиве - 100.
|
||||
|
||||
Массив инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$item_array = new AtolOnline\Entities\ItemArray();
|
||||
```
|
||||
|
||||
Чтобы задать содержимое массива, используйте метод `set()`:
|
||||
|
||||
```php
|
||||
$item_array->set([
|
||||
$item_object1,
|
||||
$item_object2
|
||||
]);
|
||||
```
|
||||
|
||||
Очистить его можно передачей в сеттер пустого массива:
|
||||
|
||||
```php
|
||||
$item_array->set([]);
|
||||
```
|
||||
|
||||
Чтобы добавить объект к существующим элементам массива, используйте метод `add()`:
|
||||
|
||||
```php
|
||||
$item = new AtolOnline\Entities\Item('Банан', 100, 1);
|
||||
$item_array->add($item);
|
||||
```
|
||||
|
||||
Методы `set()` и `add()` проверяют количество элементов в массиве перед его обновлением.
|
||||
Выбрасывают исключение `AtolTooManyItemsException` (если в массиве уже максимальное количество объектов).
|
||||
|
||||
Чтобы получить содержимое массива, используйте метод `get()`:
|
||||
|
||||
```php
|
||||
$item_array->get();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $item_array;
|
||||
$json_string = (string)$item_array;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $item_array->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
296
docs/kkt.md
296
docs/kkt.md
@ -1,296 +0,0 @@
|
||||
# Работа с ККТ
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
Объект ККТ инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$kkt = new AtolOnline\Api\Kkt();
|
||||
```
|
||||
|
||||
## Настройка ККТ
|
||||
|
||||
Для работы с облачной ККТ необходимы следующие параметры:
|
||||
* логин кассы;
|
||||
* пароль кассы;
|
||||
* код группы кассы;
|
||||
|
||||
Чтоб получить их, нужно:
|
||||
1. авторизоваться на [online.atol.ru](https://online.atol.ru/lk/Account/Login);
|
||||
2. на странице [Мои компании](https://online.atol.ru/lk/Company/List) нажать кнопку **Настройки интегратора**.
|
||||
Скачается XML-файл с нужными настройками.
|
||||
|
||||
Установить эти параметры можно двумя путями:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$kkt = new AtolOnline\Api\Kkt($group, $login, $password);
|
||||
|
||||
// 2 способ - через сеттеры
|
||||
$kkt = (new AtolOnline\Api\Kkt())
|
||||
->setLogin($login)
|
||||
->setGroup($group)
|
||||
->setPassword($password);
|
||||
```
|
||||
|
||||
Получить заданные параметры можно через соответствующие геттеры:
|
||||
|
||||
```php
|
||||
$kkt->getLogin();
|
||||
$kkt->getPassword();
|
||||
$kkt->getGroup();
|
||||
```
|
||||
|
||||
Также для работы потребуются:
|
||||
* ИНН продавца;
|
||||
* URL места расчёта (ссылка на ваш интернет-сервис).
|
||||
|
||||
Эти параметры нужно задать [объекту компании](/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).
|
||||
|
||||
В библиотеке есть переключатель настроек ККТ.
|
||||
С его помощью можете поменять вашу боевую ККТ на тестовую и наоборот.
|
||||
Это можно сделать одним из следующих способов:
|
||||
|
||||
```php
|
||||
// включить в любом месте кода:
|
||||
$kkt->setTestMode();
|
||||
$kkt->setTestMode(true);
|
||||
$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()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->sell($document);
|
||||
```
|
||||
|
||||
Для регистрации документа **возврата прихода** необходимо вызвать метод `sellRefund()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->sellRefund($document);
|
||||
```
|
||||
|
||||
Для регистрации документа **расхода** необходимо вызвать метод `buy()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->buy($document);
|
||||
```
|
||||
|
||||
Для регистрации документа **возврата расхода** необходимо вызвать метод `buyRefund()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->buyRefund($document);
|
||||
```
|
||||
|
||||
Для операций, перечисленных выше, документы не должны содержать [данных коррекции](/docs/documents.md#correction).
|
||||
Тогда как для операций коррекции, которые описаны ниже, эти данные должны присутствовать.
|
||||
|
||||
Для регистрации документа **коррекции прихода** необходимо вызвать метод `sellCorrection()`:
|
||||
|
||||
```php
|
||||
$result = $kkt->sellCorrection($document);
|
||||
```
|
||||
|
||||
Для регистрации документа **коррекции расхода** необходимо вызвать метод `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`.
|
||||
|
||||
Этот же объект можно получить через геттер `getLastResponse()`.
|
||||
|
||||
Этот объект содержит в себе HTTP-код ответа, массив заголовков и JSON-декодированные данные тела ответа.
|
||||
|
||||
```php
|
||||
$result = $kkt->getLastResponse(); // вернёт последний ответ от API
|
||||
$headers = $result->getHeaders(); // вернёт заголовки
|
||||
$code = $result->getCode(); // вернёт код ответа
|
||||
$body = $result->getContent(); // вернёт JSON-декодированное тело ответа
|
||||
```
|
||||
|
||||
Обращаться к полям JSON-декодированного объекта можно опуская вызов метода `getContent()` таким образом:
|
||||
|
||||
```php
|
||||
// вернёт значение поля uuid
|
||||
$uuid = $result->getContent()->uuid;
|
||||
$uuid = $result->uuid;
|
||||
// вернёт текст ошибки
|
||||
$err_text = $result->getContent()->error->text;
|
||||
$err_text = $result->error->text;
|
||||
```
|
||||
|
||||
Проверка корректности ответа (отсутствия ошибок) работает через метод `isValid()`:
|
||||
|
||||
```php
|
||||
$kkt->getLastResponse()->isValid(); // вернёт true, если ошибок нет
|
||||
```
|
||||
|
||||
## Проверка статуса документа
|
||||
|
||||
Если перед отправкой документа на регистрацию был задан callback_url через метод `setCallbackUrl()`, то ответ придёт на указанный адрес автоматически, как только документ обработается на стороне ККТ.
|
||||
Ответ может быть как об успешной регистрации, так и ошибочной.
|
||||
|
||||
В любом случае, вам доступны два метода, с помощью которых вы можете проверять статус документа самостоятельно:
|
||||
|
||||
```php
|
||||
$kkt->getDocumentStatus();
|
||||
$kkt->pollDocumentStatus();
|
||||
```
|
||||
|
||||
Эти методы принимают на вход `uuid` кода регистрации.
|
||||
Этот UUID можно получить из ответа, полученного при отправке документа на регистрацию:
|
||||
|
||||
```php
|
||||
$sell_result = $kkt->sell($document);
|
||||
$kkt->pollDocumentStatus($sell_result->uuid);
|
||||
$kkt->getDocumentStatus($sell_result->uuid);
|
||||
```
|
||||
|
||||
Метод `pollDocumentStatus()` многократно опрашивает ККТ на предмет состояния документа.
|
||||
Метод может принимать до трёх параметров:
|
||||
* uuid;
|
||||
* количество попыток (по умолчанию — 5);
|
||||
* время между попытками в секундах (по умолчанию — 1).
|
||||
|
||||
```php
|
||||
// Проверять статус 10 раз на протяжении 20 секунд — каждые две секунды
|
||||
$kkt->pollDocumentStatus($sell_result->uuid, 10, 20);
|
||||
```
|
||||
|
||||
Учитывайте, что метод вернёт результат как только сменится статус регистрации на успешный или ошибочный.
|
||||
|
||||
Использовать его лучше сразу после отправки документа на регистрацию (как в примере выше).
|
||||
|
||||
> Как правило, полная регистрация одного документа занимает 4-5 секунд.
|
||||
|
||||
Метод `getDocumentStatus()` принимает на вход только `uuid` и запрашивает состояние документа лишь единожды.
|
||||
Использовать его целесообразнее в те моменты, когда нет необходимости знать успех регистрации сразу после отправки документа.
|
||||
|
||||
> Обратите внимание, что АТОЛ позволяет получать статус документа в течение 32 суток с момента его регистрации.
|
||||
|
||||
Методы `pollDocumentStatus()` и `getDocumentStatus()` возвращают объект `AtolOnline\Api\KktResponse`.
|
||||
Оба выбрасывают исключение `AtolUuidValidateException` (если переданная строка UUID невалидна).
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $item;
|
||||
$json_string = (string)$item;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $item->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
118
docs/monitoring.md
Normal file
118
docs/monitoring.md
Normal file
@ -0,0 +1,118 @@
|
||||
# Мониторинг ККТ
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
||||
|
||||
---
|
||||
|
||||
Библиотека предоставляет возможность следить за состоянием ваших облачных ККТ через API.
|
||||
|
||||
## Инициализация
|
||||
|
||||
Для этого следует использовать класс `KktMonitor`:
|
||||
|
||||
```php
|
||||
// можно передать параметры подключения в конструктор
|
||||
$monitor = new AtolOnline\Api\Monitor(
|
||||
login: 'mylogin',
|
||||
password: 'qwerty'
|
||||
);
|
||||
|
||||
// можно - отдельными сеттерами
|
||||
$monitor = new AtolOnline\Api\Monitor();
|
||||
->setLogin($credentials['login'])
|
||||
->setPassword($credentials['password']);
|
||||
```
|
||||
|
||||
Логин и пароль для мониторинга те же, что для регистрации документов.
|
||||
|
||||
**По умолчанию монитор работает в тестовом режиме.**
|
||||
Перевести его в боевой режим можно:
|
||||
|
||||
```php
|
||||
// передачей в конструктор `false` первым параметром:
|
||||
$monitor = new AtolOnline\Api\Monitor(false, /*...*/);
|
||||
|
||||
// или отдельным сеттером
|
||||
$monitor->setTestMode(false);
|
||||
```
|
||||
|
||||
**Тестовый режим** нужен для проверки работоспособности библиотеки и API АТОЛ.
|
||||
|
||||
**В боевом режиме** можно получать данные по своим ККТ.
|
||||
|
||||
## Получение данных обо всех своих ККТ
|
||||
|
||||
Для получения данных обо всех своих ККТ следует вызвать метод `AtolOnline\Api\KktMonitor::getAll()`:
|
||||
|
||||
```php
|
||||
$kkts = $monitor->getAll();
|
||||
```
|
||||
|
||||
В ответе будет итерируемая коллекция объектов `AtolOnline\Entities\Kkt`. Каждый из этих объектов содержит атрибуты:
|
||||
|
||||
```php
|
||||
// для примера получим первую ККТ из всех
|
||||
$kkt = $kkts->first();
|
||||
|
||||
// посмотрим на её атрибуты:
|
||||
$kkt->serialNumber; // Заводской номер ККТ
|
||||
$kkt->registrationNumber; // Регистрационный номер машины (РНМ)
|
||||
$kkt->deviceNumber; // Номер автоматического устройства (внутренний идентификатор устройства)
|
||||
$kkt->fiscalizationDate; // Дата активации (фискализации) ФН с указанием таймзоны
|
||||
$kkt->fiscalStorageExpiration; // Дата замены ФН (Срок действия ФН), с указанием таймзоны
|
||||
$kkt->signedDocuments; // Количество подписанных документов в ФН
|
||||
$kkt->fiscalStoragePercentageUse; // Наполненость ФН в %
|
||||
$kkt->fiscalStorageINN; // ИНН компании (указанный в ФН)
|
||||
$kkt->fiscalStorageSerialNumber; // Заводской (серийный) номер ФН
|
||||
$kkt->fiscalStoragePaymentAddress; // Адрес расчёта, указанный в ФН
|
||||
$kkt->groupCode; // Код группы кассы
|
||||
$kkt->timestamp; // Время и дата формирования данных, UTC
|
||||
$kkt->isShiftOpened; // Признак открыта смена (true) или закрыта (false)
|
||||
$kkt->shiftNumber; // Номер смены (или "Номер закрытой смены", когда смена закрыта)
|
||||
$kkt->shiftReceipt; // Номер документа за смену (или "Кол-во чеков закрытой смены", когда смена закрыта)
|
||||
$kkt->unsentDocs; // Количество неотправленных документов. Указывается, если значение отлично от 0.
|
||||
$kkt->firstUnsetDocTimestamp; // Дата первого неотправленного документа. Указывается, если есть неотправленные документы.
|
||||
$kkt->networkErrorCode; // Код ошибки сети
|
||||
```
|
||||
|
||||
Эти поля описаны в документации мониторинга на [стр. 11](https://online.atol.ru/files/API_service_information.pdf)
|
||||
|
||||
Сопоставления кодов ошибок и их описаний доступны в массиве `AtolOnline\Entities\Kkt::ERROR_CODES`.
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительно:
|
||||
|
||||
```php
|
||||
echo $kkt;
|
||||
$json_string = (string)$kkt;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $kkt->jsonSerialize();
|
||||
```
|
||||
|
||||
## Получение данных об одной из своих ККТ
|
||||
|
||||
Для этого следует вызвать метод `AtolOnline\Api\KktMonitor::getOne()`, передав на вход серийный номер (`serialNumber`)
|
||||
нужной ККТ:
|
||||
|
||||
```php
|
||||
$kkt = $monitor->getOne($kkts->first()->serialNumber);
|
||||
```
|
||||
|
||||
Метод вернёт единственный объект `AtolOnline\Entities\Kkt` с атрибутами, описанными выше.
|
||||
|
||||
## Получение последнего ответа от сервера
|
||||
|
||||
Класс `AtolOnline\Api\KktMonitor` расширяет абстрактный класс `AtolOnline\Api\AtolClient`.
|
||||
|
||||
Это значит, что последний ответ от API АТОЛ всегда сохраняется объектом класса `AtolOnline\Api\AtolReponse`. К нему
|
||||
можно обратиться через метод `AtolOnline\Api\KktMonitor::getResponse()`, независимо от того, что возвращают другие
|
||||
методы монитора.
|
||||
|
||||
---
|
||||
|
||||
Читай также: [Обработка ответа API](response.md)
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
134
docs/payments.md
134
docs/payments.md
@ -1,134 +0,0 @@
|
||||
# Работа с оплатами
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
## Один объект
|
||||
|
||||
Объект оплаты инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$payment = new AtolOnline\Entities\Payment();
|
||||
```
|
||||
|
||||
У объекта оплаты должны быть указаны все следующие атрибуты:
|
||||
* тип оплаты (теги ФФД: 1031, 1081, 1215, 1216, 1217) - все типы перечислены в классе `AtolOnline\Constants\PaymentTypes` (по умолчанию `ELECTRON`)
|
||||
* сумма оплаты (теги ФФД: 1031, 1081, 1215, 1216, 1217; по умолчанию 0)
|
||||
|
||||
> Все эти атрибуты являются **обязательными**.
|
||||
|
||||
Установить атрибуты можно следующими способами:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$payment = new AtolOnline\Entities\Payment(
|
||||
AtolOnline\Constants\PaymentTypes::OTHER, // тип оплаты
|
||||
123.45 // сумма оплаты
|
||||
);
|
||||
|
||||
// 2 способ - через сеттер
|
||||
$payment = (new AtolOnline\Entities\Payment())
|
||||
->setType(AtolOnline\Constants\PaymentTypes::OTHER) // тип оплаты
|
||||
->setSum(123.45); // сумма оплаты
|
||||
```
|
||||
|
||||
Размер налога высчитывается автоматически из общей суммы.
|
||||
Сумму, от которой нужно расчитать размер налога, можно передать следующими способами:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$payment = new AtolOnline\Entities\Payment(
|
||||
AtolOnline\Constants\PaymentTypes::CASH, // тип оплаты
|
||||
1234.56 // сумма оплаты в рублях
|
||||
);
|
||||
|
||||
// 2 способ - через сеттер
|
||||
$payment = (new AtolOnline\Entities\Payment())
|
||||
->setType(AtolOnline\Constants\PaymentTypes::CASH) // тип оплаты
|
||||
->setSum(1234.56); // сумма оплаты в рублях
|
||||
```
|
||||
|
||||
Получить установленную сумму оплаты в рублях можно через геттер `getSum()`:
|
||||
|
||||
```php
|
||||
var_dump($payment->getSum());
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $payment;
|
||||
$json_string = (string)$payment;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $payment->jsonSerialize();
|
||||
```
|
||||
|
||||
<a name="array"></a>
|
||||
## Массив объектов оплат
|
||||
|
||||
> Максимальное количество объектов в массиве - 10.
|
||||
|
||||
Массив инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$payment_array = new AtolOnline\Entities\PaymentArray();
|
||||
```
|
||||
|
||||
Чтобы задать содержимое массива, используйте метод `set()`:
|
||||
|
||||
```php
|
||||
use AtolOnline\{Constants\PaymentTypes, Entities\Payment};
|
||||
|
||||
$payment_array->set([
|
||||
new Payment(PaymentTypes::ELECTRON, 123),
|
||||
new Payment(PaymentTypes::ELECTRON, 53.2),
|
||||
new Payment(PaymentTypes::ELECTRON, 23.99),
|
||||
new Payment(PaymentTypes::ELECTRON, 11.43)
|
||||
]);
|
||||
```
|
||||
|
||||
Очистить его можно передачей в сеттер пустого массива:
|
||||
|
||||
```php
|
||||
$payment_array->set([]);
|
||||
```
|
||||
|
||||
Чтобы добавить объект к существующим элементам массива, используйте метод `add()`:
|
||||
|
||||
```php
|
||||
use AtolOnline\{Constants\PaymentTypes, Entities\Payment};
|
||||
|
||||
$payment = new Payment(PaymentTypes::PRE_PAID, 20);
|
||||
$payment_array->add($payment);
|
||||
```
|
||||
|
||||
Методы `set()` и `add()` проверяют количество элементов в массиве перед его обновлением.
|
||||
Выбрасывают исключение `AtolTooManyPaymentsException` (если в массиве уже максимальное количество объектов).
|
||||
|
||||
Чтобы получить содержимое массива, используйте метод `get()`:
|
||||
|
||||
```php
|
||||
$payment_array->get();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $payment_array;
|
||||
$json_string = (string)$payment_array;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $payment_array->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
@ -1,15 +1,49 @@
|
||||
# Документация к библиотеке atol-online
|
||||
# Документация к библиотеке
|
||||
|
||||
Содержание:
|
||||
1. [Работа с клиентами (покупателями)](client.md)
|
||||
2. [Работа с компанией (продавцом)](company.md)
|
||||
3. [Работа с оплатами](payments.md)
|
||||
4. [Работа со ставками НДС](vats.md)
|
||||
5. [Работа с предметами расчёта](items.md)
|
||||
6. [Работа с данными коррекции](correction_info.md)
|
||||
7. [Работа с документами](documents.md)
|
||||
8. [Работа с ККТ](kkt.md)
|
||||
<a id="toc"></a>
|
||||
|
||||
---
|
||||
## Содержание
|
||||
|
||||
Если вы нашли опечатку или какое-то несоответствие — делайте pull-request.
|
||||
* [Общий алгоритм](#getstarted)
|
||||
* [Сущность](entity.md)
|
||||
* [Коллекция сущностей](collection.md)
|
||||
* [Мониторинг ККТ](monitoring.md)
|
||||
* [Фискализация документа](fiscalizing.md)
|
||||
* [Обработка ответа API](response.md)
|
||||
|
||||
Если вы нашли опечатку или какое-то несоответствие — делайте pull-request.
|
||||
|
||||
<a id="getstarted"></a>
|
||||
|
||||
## Общий алгоритм
|
||||
|
||||
1. Создать документ `AtolOnline\Entities\Receipt` или `AtolOnline\Entities\Correction`, добавив в него все необходимые
|
||||
данные
|
||||
2. Отправить документ на регистрацию:
|
||||
2.1. *Необязательно:* при отправке задать `callback_url`, на который АТОЛ отправит HTTP POST о состоянии документа;
|
||||
2.2. *Необязательно:* при отправке задать `external_id`, чтобы присвоить свой уникальный идентификатор документа;
|
||||
3. Запомнить `uuid` из пришедшего ответа, поскольку он пригодится для последующей проверки статуса фискализации.
|
||||
> Если с документом был передан `callback_url`, то ответ придёт на этот самый URL.
|
||||
> Он должен быть обработан вашим сервисом в соответствии с бизнес-процессом.
|
||||
|
||||
> Если с документом **не был** передан `callback_url` **либо** callback от АТОЛа не пришёл в течение 300 секунд
|
||||
> (5 минут), нужно запрашивать вручную по `uuid`, пришедшему от АТОЛа в ответ на регистрацию документа.
|
||||
4. Проверить состояние документа:
|
||||
4.1. взять `uuid` ответа, полученного на запрос фискализации;
|
||||
4.2. отправить его в запросе состояния документа.
|
||||
> Данные о документе можно получить только в течение 32 суток после успешной фискализации.
|
||||
|
||||
В зависимости от специфики бизнеса, в документах можно/нужно передавать разную информацию. Подробности в документации
|
||||
АТОЛ Онлайн и исходниках библиотеки.
|
||||
|
||||
## Об отправке электронного чека покупателю
|
||||
|
||||
После успешной фискализации документа покупатель автоматически получит уведомление **от ОФД**, который используется в
|
||||
связке с вашей ККТ:
|
||||
|
||||
* **по email**, если в документе указан email клиента;
|
||||
* **по смс**:
|
||||
* если в документе указан номер телефона клиента;
|
||||
* если на стороне ОФД необходима и подключена услуга СМС-информирования (уточняйте подробности о своего ОФД).
|
||||
|
||||
> Если заданы email и телефон, то ОФД отдаёт приоритет email.
|
||||
|
57
docs/response.md
Normal file
57
docs/response.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Обработка ответа API
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
||||
|
||||
---
|
||||
|
||||
Объект класса `AtolOnline\Api\AtolResponse` возвращается всеми методами, которые обращаются к АТОЛ Онлайн API.
|
||||
|
||||
Поскольку классы `AtolOnline\Api\Fiscalizer` и `AtolOnline\Api\KktMonitor` наследуются от
|
||||
абстрактного `AtolOnline\Api\AtolClient`, они оба предоставляют метод `getLastReponse()` для возврата последнего
|
||||
полученного ответа от API.
|
||||
|
||||
Таким образом, а общем случае необязательно сразу сохранять ответ в переменную:
|
||||
|
||||
```php
|
||||
$response = $kkt->sell($receipt);
|
||||
```
|
||||
|
||||
Достаточно обратиться к нему позднее:
|
||||
|
||||
```php
|
||||
$kkt->sell($receipt);
|
||||
// ...
|
||||
$response = $kkt->getLastResponse();
|
||||
```
|
||||
|
||||
Однако, при сложной логике и многократных запросах следует пользоваться этим с умом и осторожностью.
|
||||
|
||||
Объект `AtolResponse` содержит в себе HTTP-код ответа, массив заголовков и JSON-декодированные данные тела ответа.
|
||||
|
||||
```php
|
||||
$headers = $response->getHeaders(); // вернёт заголовки
|
||||
$code = $response->getCode(); // вернёт код ответа
|
||||
$body = $response->getContent(); // вернёт JSON-декодированный объект тела ответа
|
||||
```
|
||||
|
||||
Обращаться к полям тела ответа можно опуская вызов метода `getContent()`:
|
||||
|
||||
```php
|
||||
// вернёт значение поля uuid
|
||||
$uuid = $response->getContent()->uuid;
|
||||
$uuid = $response->uuid;
|
||||
|
||||
// вернёт текст ошибки
|
||||
$err_text = $response->getContent()->error->text;
|
||||
$err_text = $response->error->text;
|
||||
```
|
||||
|
||||
Проверка успешности операции доступна через метод `isSuccessful()`:
|
||||
|
||||
```php
|
||||
$response->isSuccessful(); // вернёт true, если ошибок нет
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md#toc)
|
194
docs/vats.md
194
docs/vats.md
@ -1,194 +0,0 @@
|
||||
# Работа со ставками НДС
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
||||
|
||||
---
|
||||
|
||||
## Один объект
|
||||
|
||||
Объект ставки НДС инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$vat = new AtolOnline\Entities\Vat();
|
||||
```
|
||||
|
||||
У объекта ставки должны быть указаны все следующие атрибуты:
|
||||
* тип ставки (теги ФФД: 1199 для предмета расчёта; 1105, 1104, 1103, 1102, 1107, 1106 для чека) - все типы перечислены в классе `AtolOnline\Constants\VatTypes` (по умолчанию `NONE`)
|
||||
* размер налога (теги ФФД: 1200 для предмета расчёта; 1105, 1104, 1103, 1102, 1107, 1106 для чека)
|
||||
|
||||
> Все эти атрибуты являются **обязательными**.
|
||||
|
||||
Установить тип ставки НДС можно следующими способами:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$vat = new AtolOnline\Entities\Vat(
|
||||
AtolOnline\Constants\VatTypes::VAT20 // тип ставки
|
||||
);
|
||||
|
||||
// 2 способ - через сеттер
|
||||
$vat = (new AtolOnline\Entities\Vat())
|
||||
->setType(AtolOnline\Constants\VatTypes::VAT20); // тип ставки
|
||||
```
|
||||
|
||||
Размер налога высчитывается автоматически из общей суммы.
|
||||
Сумму, от которой нужно расчитать размер налога, можно передать следующими способами:
|
||||
|
||||
```php
|
||||
// 1 способ - через конструктор
|
||||
$vat = new AtolOnline\Entities\Vat(
|
||||
AtolOnline\Constants\VatTypes::VAT10, // тип ставки
|
||||
1234.56 // общая сумма в рублях
|
||||
);
|
||||
|
||||
// 2 способ - через сеттер
|
||||
$vat = (new AtolOnline\Entities\Vat())
|
||||
->setType(AtolOnline\Constants\VatTypes::VAT10) // тип ставки
|
||||
->setSum(150); // общая сумма в рублях
|
||||
```
|
||||
|
||||
Сумму можно установить и до установки типа ставки.
|
||||
Объект её запомнит и пересчитает итоговый размер налога при смене типа ставки:
|
||||
|
||||
```php
|
||||
$vat = (new AtolOnline\Entities\Vat())
|
||||
->setSum(150) // общая сумма в рублях
|
||||
->setType(AtolOnline\Constants\VatTypes::VAT10); // тип ставки
|
||||
```
|
||||
|
||||
Получить установленную расчётную сумму в рублях можно через геттер `getSum()`:
|
||||
|
||||
```php
|
||||
var_dump($vat->getSum());
|
||||
// double(150)
|
||||
```
|
||||
|
||||
Получить расчитанный размер налога в рублях можно через геттер `getFinalSum()`:
|
||||
|
||||
```php
|
||||
var_dump($vat->getFinalSum());
|
||||
// double(15): для примера выше это 10% от 150р = 15р
|
||||
```
|
||||
|
||||
Общую сумму, из которой расчитывается размер налога, можно увеличить, используя метод `addSum()`.
|
||||
Указанная в рублях сумма увеличится и итоговый размер налога пересчитается.
|
||||
Для уменьшения суммы следует передать отрицательное число.
|
||||
|
||||
Разберём комплексный пример изменения типа ставки и расчётной суммы:
|
||||
|
||||
```php
|
||||
use AtolOnline\{Entities\Vat, Constants\VatTypes};
|
||||
|
||||
$vat = new Vat(VatTypes::VAT20, 120);
|
||||
echo "НДС20 от 120р: ";
|
||||
var_dump($vat->getFinalSum());
|
||||
|
||||
echo "НДС10 от 120р: ";
|
||||
$vat->setType(VatTypes::VAT10);
|
||||
var_dump($vat->getFinalSum());
|
||||
|
||||
$vat->addSum(40);
|
||||
echo "НДС10 от {$vat->getSum()}р: ";
|
||||
var_dump($vat->getFinalSum());
|
||||
|
||||
$vat->setType(VatTypes::VAT20)->addSum(-20);
|
||||
echo "НДС20 от {$vat->getSum()}р: ";
|
||||
var_dump($vat->getFinalSum());
|
||||
|
||||
$vat->setType(VatTypes::VAT120);
|
||||
echo "НДС20/120 от {$vat->getSum()}р: ";
|
||||
var_dump($vat->getFinalSum());
|
||||
```
|
||||
|
||||
Результат будет следующим:
|
||||
|
||||
```
|
||||
НДС20 от 120р:
|
||||
double(24)
|
||||
НДС10 от 120р:
|
||||
double(12)
|
||||
НДС10 от 160р:
|
||||
double(16)
|
||||
НДС20 от 140р:
|
||||
double(28)
|
||||
НДС20/120 от 140р:
|
||||
double(23.33)
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $vat;
|
||||
$json_string = (string)$vat;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $vat->jsonSerialize();
|
||||
```
|
||||
|
||||
<a name="array"></a>
|
||||
## Массив объектов ставок НДС
|
||||
|
||||
> Максимальное количество в массиве - 6.
|
||||
|
||||
Массив инициализируется следующим образом:
|
||||
|
||||
```php
|
||||
$vat_array = new AtolOnline\Entities\VatArray();
|
||||
```
|
||||
|
||||
Чтобы задать содержимое массива, используйте метод `set()`:
|
||||
|
||||
```php
|
||||
use AtolOnline\{Constants\VatTypes, Entities\Vat};
|
||||
|
||||
$vat_array->set([
|
||||
new Vat(VatTypes::VAT10, 123),
|
||||
new Vat(VatTypes::VAT110, 53.2),
|
||||
new Vat(VatTypes::VAT20, 23.99),
|
||||
new Vat(VatTypes::VAT120, 11.43)
|
||||
]);
|
||||
```
|
||||
|
||||
Очистить его можно передачей в сеттер пустого массива:
|
||||
|
||||
```php
|
||||
$vat_array->set([]);
|
||||
```
|
||||
|
||||
Чтобы добавить объект к существующим элементам массива, используйте метод `add()`:
|
||||
|
||||
```php
|
||||
use AtolOnline\{Constants\VatTypes, Entities\Vat};
|
||||
|
||||
$vat = new Vat(VatTypes::VAT20, 20);
|
||||
$vat_array->add($vat);
|
||||
```
|
||||
|
||||
Методы `set()` и `add()` проверяют количество элементов в массиве перед его обновлением.
|
||||
Выбрасывают исключение `AtolTooManyVatsException` (если в массиве уже максимальное количество объектов).
|
||||
|
||||
Чтобы получить содержимое массива, используйте метод `get()`:
|
||||
|
||||
```php
|
||||
$vat_array->get();
|
||||
```
|
||||
|
||||
Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`:
|
||||
|
||||
```php
|
||||
echo $vat_array;
|
||||
$json_string = (string)$vat_array;
|
||||
```
|
||||
|
||||
Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`:
|
||||
|
||||
```php
|
||||
$json_array = $vat_array->jsonSerialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Вернуться к содержанию](readme.md)
|
43
phpunit.xml
43
phpunit.xml
@ -1,25 +1,34 @@
|
||||
<?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"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<file>ClientTest.php</file>
|
||||
<file>CompanyTest.php</file>
|
||||
<file>VatTest.php</file>
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
<testsuite name="Helpers">
|
||||
<file>tests/AtolOnline/Tests/HelpersTest.php</file>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<file>ItemTest.php</file>
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
<testsuite name="Entities">
|
||||
<directory>tests/AtolOnline/Tests/Entities</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Collections">
|
||||
<directory>tests/AtolOnline/Tests/Collections</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Api">
|
||||
<directory>tests/AtolOnline/Tests/Api</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
|
331
src/Api/AtolClient.php
Normal file
331
src/Api/AtolClient.php
Normal file
@ -0,0 +1,331 @@
|
||||
<?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;
|
||||
use AtolOnline\Exceptions\{
|
||||
AuthFailedException,
|
||||
EmptyLoginException,
|
||||
EmptyPasswordException,
|
||||
TooLongLoginException,
|
||||
TooLongPasswordException};
|
||||
use AtolOnline\TestEnvParams;
|
||||
use GuzzleHttp\{
|
||||
Client,
|
||||
Exception\GuzzleException};
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* Класс для подключения АТОЛ Онлайн API
|
||||
*/
|
||||
abstract class AtolClient
|
||||
{
|
||||
/**
|
||||
* @var array Последний запрос к серверу АТОЛ
|
||||
*/
|
||||
protected array $request;
|
||||
|
||||
/**
|
||||
* @var AtolResponse|null Последний ответ сервера АТОЛ
|
||||
*/
|
||||
protected ?AtolResponse $response;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @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 array
|
||||
*/
|
||||
public function getLastRequest(): array
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает последний ответ сервера
|
||||
*
|
||||
* @return AtolResponse|null
|
||||
*/
|
||||
public function getLastResponse(): ?AtolResponse
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный флаг тестового режима
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает логин доступа к API
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
#[Pure]
|
||||
public function getLogin(): ?string
|
||||
{
|
||||
return $this->isTestMode()
|
||||
? TestEnvParams::FFD105()['login']
|
||||
: $this->login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает логин доступа к API
|
||||
*
|
||||
* @param string $login
|
||||
* @return $this
|
||||
* @throws EmptyLoginException
|
||||
* @throws TooLongLoginException
|
||||
*/
|
||||
public function setLogin(string $login): self
|
||||
{
|
||||
$login = trim($login);
|
||||
if (empty($login)) {
|
||||
throw new EmptyLoginException();
|
||||
} elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) {
|
||||
throw new TooLongLoginException($login);
|
||||
}
|
||||
$this->login = $login;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает пароль доступа к API
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
#[Pure]
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->isTestMode()
|
||||
? TestEnvParams::FFD105()['password']
|
||||
: $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает пароль доступа к API
|
||||
*
|
||||
* @param string $password
|
||||
* @return $this
|
||||
* @throws EmptyPasswordException Пароль ККТ не может быть пустым
|
||||
* @throws TooLongPasswordException Слишком длинный пароль ККТ
|
||||
*/
|
||||
public function setPassword(string $password): self
|
||||
{
|
||||
if (empty($password)) {
|
||||
throw new EmptyPasswordException();
|
||||
} elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) {
|
||||
throw new TooLongPasswordException($password);
|
||||
}
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает набор заголовков для HTTP-запроса
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[Pure]
|
||||
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->isSuccessful() || !$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 AtolResponse
|
||||
* @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
|
||||
): AtolResponse {
|
||||
$http_method = strtoupper(trim($http_method));
|
||||
$options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []);
|
||||
$http_method != 'GET' && $options['json'] = $data;
|
||||
$this->request = array_merge([
|
||||
'method' => $http_method,
|
||||
'url' => $url,
|
||||
], $options);
|
||||
$response = $this->http->request($http_method, $url, $options);
|
||||
return $this->response = new AtolResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет авторизацию на сервере АТОЛ
|
||||
* Авторизация выполнится только если неизвестен токен
|
||||
*
|
||||
* @return bool
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function auth(): bool
|
||||
{
|
||||
if (empty($this->getToken()) && $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;
|
||||
}
|
@ -1,52 +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
|
||||
*/
|
||||
|
||||
/** @noinspection PhpMultipleClassDeclarationsInspection */
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Api;
|
||||
|
||||
use JetBrains\PhpStorm\{
|
||||
ArrayShape,
|
||||
Pure};
|
||||
use JsonSerializable;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use stdClass;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Класс AtolResponse, описывающий ответ от ККТ
|
||||
*
|
||||
* @property mixed $error
|
||||
* @package AtolOnline\Api
|
||||
*/
|
||||
class KktResponse implements JsonSerializable
|
||||
final class AtolResponse implements JsonSerializable, Stringable
|
||||
{
|
||||
/**
|
||||
* @var int Код ответа сервера
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
protected int $code;
|
||||
|
||||
/**
|
||||
* @var \stdClass Содержимое ответа сервера
|
||||
* @var object|array|null Содержимое ответа сервера
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
protected object|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());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает заголовки ответа
|
||||
*
|
||||
@ -56,18 +64,19 @@ class KktResponse implements JsonSerializable
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает запрошенный параметр из декодированного объекта результата
|
||||
*
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
#[Pure]
|
||||
public function __get($name): mixed
|
||||
{
|
||||
return $this->getContent()->$name;
|
||||
return $this->getContent()?->$name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает код ответа
|
||||
*
|
||||
@ -77,42 +86,49 @@ class KktResponse implements JsonSerializable
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Возвращает объект результата запроса
|
||||
*
|
||||
* @return stdClass|null
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContent(): ?stdClass
|
||||
public function getContent(): mixed
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Проверяет успешность запроса по соержимому результата
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
#[Pure]
|
||||
public function isSuccessful(): 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
#[ArrayShape([
|
||||
'code' => 'int',
|
||||
'headers' => 'array|\string[][]',
|
||||
'body' => 'mixed',
|
||||
]
|
||||
)]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
@ -120,4 +136,4 @@ class KktResponse implements JsonSerializable
|
||||
'body' => $this->content,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
378
src/Api/Fiscalizer.php
Normal file
378
src/Api/Fiscalizer.php
Normal 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,
|
||||
TestEnvParams};
|
||||
use AtolOnline\Entities\{
|
||||
Correction,
|
||||
Receipt};
|
||||
use AtolOnline\Exceptions\{
|
||||
AuthFailedException,
|
||||
EmptyGroupException,
|
||||
EmptyLoginException,
|
||||
EmptyPasswordException,
|
||||
InvalidCallbackUrlException,
|
||||
InvalidEntityInCollectionException,
|
||||
InvalidInnLengthException,
|
||||
InvalidPaymentAddressException,
|
||||
InvalidUuidException,
|
||||
TooLongCallbackUrlException,
|
||||
TooLongLoginException,
|
||||
TooLongPasswordException,
|
||||
TooLongPaymentAddressException};
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
/**
|
||||
* Класс фискализатора для регистрации документов на ККТ
|
||||
*/
|
||||
final class Fiscalizer 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
|
||||
* @throws EmptyGroupException
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает группу доступа к ККТ в соответствии с флагом тестового режима
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
#[Pure]
|
||||
public function getGroup(): ?string
|
||||
{
|
||||
return $this->isTestMode()
|
||||
? TestEnvParams::FFD105()['group']
|
||||
: $this->group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает группу доступа к ККТ
|
||||
*
|
||||
* @param string $group
|
||||
* @return $this
|
||||
* @throws EmptyGroupException
|
||||
*/
|
||||
public function setGroup(string $group): self
|
||||
{
|
||||
// критерии к длине строки не описаны ни в схеме, ни в документации
|
||||
empty($group = trim($group)) && throw new EmptyGroupException();
|
||||
$this->group = $group;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает URL для приёма колбеков
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCallbackUrl(): ?string
|
||||
{
|
||||
return $this->callback_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает URL для приёма колбеков
|
||||
*
|
||||
* @param string|null $url
|
||||
* @return $this
|
||||
* @throws TooLongCallbackUrlException
|
||||
* @throws InvalidCallbackUrlException
|
||||
*/
|
||||
public function setCallbackUrl(?string $url = null): self
|
||||
{
|
||||
$url = trim((string)$url);
|
||||
if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) {
|
||||
throw new TooLongCallbackUrlException($url);
|
||||
} elseif (!empty($url) && !preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) {
|
||||
throw new InvalidCallbackUrlException();
|
||||
}
|
||||
$this->callback_url = $url ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ прихода
|
||||
*
|
||||
* @param Receipt $receipt Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function sell(Receipt $receipt, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $this->registerDocument('sell', $receipt, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ возврата прихода
|
||||
*
|
||||
* @param Receipt $receipt Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function sellRefund(Receipt $receipt, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $this->registerDocument('sell_refund', $receipt, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ коррекции прихода
|
||||
*
|
||||
* @param Correction $correction Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function sellCorrect(Correction $correction, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $this->registerDocument('sell_correction', $correction, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ расхода
|
||||
*
|
||||
* @param Receipt $receipt Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function buy(Receipt $receipt, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $this->registerDocument('buy', $receipt, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ возврата расхода
|
||||
*
|
||||
* @param Receipt $receipt Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function buyRefund(Receipt $receipt, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $this->registerDocument('buy_refund', $receipt, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ коррекции расхода
|
||||
*
|
||||
* @param Correction $correction Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function buyCorrect(Correction $correction, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $this->registerDocument('buy_correction', $correction, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет статус чека на ККТ один раз
|
||||
*
|
||||
* @param string $uuid UUID регистрации
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidUuidException
|
||||
*/
|
||||
public function getDocumentStatus(string $uuid): ?AtolResponse
|
||||
{
|
||||
!Uuid::isValid($uuid = trim($uuid)) && throw new InvalidUuidException($uuid);
|
||||
return $this->auth()
|
||||
? $this->sendRequest('GET', $this->getFullUrl('report/' . $uuid))
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет статус чека на ККТ нужное количество раз с указанным интервалом.
|
||||
* Вернёт результат как только при очередной проверке сменится статус регистрации документа.
|
||||
*
|
||||
* @param string $uuid UUID регистрации
|
||||
* @param int $retry_count Количество попыток
|
||||
* @param int $timeout Таймаут в секундах между попытками
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidUuidException
|
||||
*/
|
||||
public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): ?AtolResponse
|
||||
{
|
||||
$try = 0;
|
||||
do {
|
||||
$response = $this->getDocumentStatus($uuid);
|
||||
if ($response->isSuccessful() && $response->getContent()->status == 'done') {
|
||||
break;
|
||||
} else {
|
||||
sleep($timeout);
|
||||
}
|
||||
++$try;
|
||||
} while ($try < $retry_count);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправляет документ на регистрацию
|
||||
*
|
||||
* @param string $api_method Метод API
|
||||
* @param Receipt|Correction $document Документ
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
protected function registerDocument(
|
||||
string $api_method,
|
||||
Receipt|Correction $document,
|
||||
?string $external_id = null
|
||||
): ?AtolResponse {
|
||||
$this->isTestMode() && $document->getCompany()
|
||||
->setInn(TestEnvParams::FFD105()['inn'])
|
||||
->setPaymentAddress(TestEnvParams::FFD105()['payment_address']);
|
||||
$this->isTestMode() && $document instanceof Receipt
|
||||
&& $document->getClient()->setInn(TestEnvParams::FFD105()['inn']);
|
||||
$this->getCallbackUrl() && $data['service'] = ['callback_url' => $this->getCallbackUrl()];
|
||||
return $this->auth()
|
||||
? $this->sendRequest(
|
||||
'POST',
|
||||
$this->getFullUrl($api_method),
|
||||
array_merge($data ?? [], [
|
||||
'timestamp' => date('d.m.Y H:i:s'),
|
||||
'external_id' => $external_id ?: Uuid::uuid4()->toString(),
|
||||
$document::DOC_TYPE => $document->jsonSerialize(),
|
||||
])
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
protected function getAuthEndpoint(): string
|
||||
{
|
||||
return $this->isTestMode()
|
||||
? 'https://testonline.atol.ru/possystem/v4/getToken'
|
||||
: 'https://online.atol.ru/possystem/v4/getToken';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
protected function getMainEndpoint(): string
|
||||
{
|
||||
return $this->isTestMode()
|
||||
? 'https://testonline.atol.ru/possystem/v4/'
|
||||
: 'https://online.atol.ru/possystem/v4/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает полный URL метода API
|
||||
*
|
||||
* @param string $api_method
|
||||
* @return string
|
||||
*/
|
||||
#[Pure]
|
||||
protected function getFullUrl(string $api_method): string
|
||||
{
|
||||
return $this->getMainEndpoint() . $this->getGroup() . '/' . trim($api_method);
|
||||
}
|
||||
}
|
129
src/Api/Monitor.php
Normal file
129
src/Api/Monitor.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?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\{
|
||||
AuthFailedException,
|
||||
EmptyLoginException,
|
||||
EmptyMonitorDataException,
|
||||
EmptyPasswordException,
|
||||
NotEnoughMonitorDataException};
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Support\Collection;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* Класс для мониторинга ККТ
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_service_information.pdf Документация
|
||||
*/
|
||||
final class Monitor extends AtolClient
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
protected function getAuthEndpoint(): string
|
||||
{
|
||||
return $this->isTestMode()
|
||||
? 'https://testonline.atol.ru/api/auth/v1/gettoken'
|
||||
: 'https://online.atol.ru/api/auth/v1/gettoken';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
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 AtolResponse|null
|
||||
* @throws GuzzleException
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9
|
||||
*/
|
||||
protected function fetchAll(?int $limit = null, ?int $offset = null): ?AtolResponse
|
||||
{
|
||||
$params = [];
|
||||
!is_null($limit) && $params['limit'] = $limit;
|
||||
!is_null($offset) && $params['offset'] = $offset;
|
||||
return $this->auth()
|
||||
? $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает информацию обо всех ККТ и ФН в рамках группы
|
||||
*
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
* @return Collection
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @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 AtolResponse
|
||||
* @throws GuzzleException
|
||||
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11
|
||||
*/
|
||||
protected function fetchOne(string $serial_number): AtolResponse
|
||||
{
|
||||
return $this->sendRequest(
|
||||
'GET',
|
||||
self::getUrlToMethod('cash-registers') . '/' . trim($serial_number),
|
||||
options: [
|
||||
'headers' => [
|
||||
'Accept' => 'application/hal+json',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает информацию о конкретной ККТ по её серийному номеру
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
@ -1,576 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Api;
|
||||
|
||||
use AtolOnline\{Constants\Constraints,
|
||||
Constants\TestEnvParams,
|
||||
Entities\Company,
|
||||
Entities\Document,
|
||||
Exceptions\AtolAuthFailedException,
|
||||
Exceptions\AtolCallbackUrlTooLongException,
|
||||
Exceptions\AtolCorrectionInfoException,
|
||||
Exceptions\AtolInvalidCallbackUrlException,
|
||||
Exceptions\AtolInvalidUuidException,
|
||||
Exceptions\AtolKktLoginEmptyException,
|
||||
Exceptions\AtolKktLoginTooLongException,
|
||||
Exceptions\AtolKktPasswordEmptyException,
|
||||
Exceptions\AtolKktPasswordTooLongException,
|
||||
Exceptions\AtolWrongDocumentTypeException
|
||||
};
|
||||
use GuzzleHttp\Client;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
/**
|
||||
* Класс для отправки запросов на ККТ
|
||||
*
|
||||
* @package AtolOnline\Api
|
||||
*/
|
||||
class Kkt extends Client
|
||||
{
|
||||
/**
|
||||
* @var bool Флаг тестового режима работы
|
||||
*/
|
||||
protected $is_test_mode = false;
|
||||
|
||||
/**
|
||||
* @var array Настройки доступа к ККТ
|
||||
*/
|
||||
protected $kkt_config = [];
|
||||
|
||||
/**
|
||||
* @var \AtolOnline\Api\KktResponse|null Последний ответ сервера АТОЛ
|
||||
*/
|
||||
protected $last_response;
|
||||
|
||||
/**
|
||||
* @var string|null Токен авторизации
|
||||
*/
|
||||
private $auth_token;
|
||||
|
||||
/**
|
||||
* Kkt constructor.
|
||||
*
|
||||
* @param string|null $group
|
||||
* @param string|null $login
|
||||
* @param string|null $pass
|
||||
* @param bool $test_mode Флаг тестового режима
|
||||
* @param array $guzzle_config Конфигурация GuzzleHttp
|
||||
* @throws \AtolOnline\Exceptions\AtolKktLoginEmptyException Логин ККТ не может быть пустым
|
||||
* @throws \AtolOnline\Exceptions\AtolKktLoginTooLongException Слишком длинный логин ККТ
|
||||
* @throws \AtolOnline\Exceptions\AtolKktPasswordEmptyException Пароль ККТ не может быть пустым
|
||||
* @throws \AtolOnline\Exceptions\AtolKktPasswordTooLongException Слишком длинный пароль ККТ
|
||||
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
|
||||
*/
|
||||
public function __construct(
|
||||
?string $group = null,
|
||||
?string $login = null,
|
||||
?string $pass = null,
|
||||
bool $test_mode = false,
|
||||
array $guzzle_config = []
|
||||
) {
|
||||
$this->resetKktConfig();
|
||||
if ($group) {
|
||||
$this->setGroup($group);
|
||||
}
|
||||
if ($login) {
|
||||
$this->setLogin($login);
|
||||
}
|
||||
if ($login) {
|
||||
$this->setPassword($pass);
|
||||
}
|
||||
$this->setTestMode($test_mode);
|
||||
$guzzle_config['base_uri'] = $this->getEndpoint();
|
||||
$guzzle_config['http_errors'] = $guzzle_config['http_errors'] ?? false;
|
||||
parent::__construct($guzzle_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает группу доступа к ККТ
|
||||
*
|
||||
* @param string $group
|
||||
* @return $this
|
||||
*/
|
||||
public function setGroup(string $group)
|
||||
{
|
||||
$this->kkt_config['prod']['group'] = $group;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает группу доступа к ККТ в соответствии с флагом тестового режима
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGroup(): string
|
||||
{
|
||||
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['group'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает логин доступа к ККТ
|
||||
*
|
||||
* @param string $login
|
||||
* @return $this
|
||||
* @throws \AtolOnline\Exceptions\AtolKktLoginEmptyException Логин ККТ не может быть пустым
|
||||
* @throws \AtolOnline\Exceptions\AtolKktLoginTooLongException Слишком длинный логин ККТ
|
||||
*/
|
||||
public function setLogin(string $login)
|
||||
{
|
||||
if (empty($login)) {
|
||||
throw new AtolKktLoginEmptyException();
|
||||
} elseif (valid_strlen($login) > Constraints::MAX_LENGTH_LOGIN) {
|
||||
throw new AtolKktLoginTooLongException($login, Constraints::MAX_LENGTH_LOGIN);
|
||||
}
|
||||
$this->kkt_config['prod']['login'] = $login;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает логин доступа к ККТ в соответствии с флагом тестового режима
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLogin(): string
|
||||
{
|
||||
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['login'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает пароль доступа к ККТ
|
||||
*
|
||||
* @param string $password
|
||||
* @return $this
|
||||
* @throws \AtolOnline\Exceptions\AtolKktPasswordEmptyException Пароль ККТ не может быть пустым
|
||||
* @throws \AtolOnline\Exceptions\AtolKktPasswordTooLongException Слишком длинный пароль ККТ
|
||||
*/
|
||||
public function setPassword(string $password)
|
||||
{
|
||||
if (empty($password)) {
|
||||
throw new AtolKktPasswordEmptyException();
|
||||
} elseif (valid_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) {
|
||||
throw new AtolKktPasswordTooLongException($password, Constraints::MAX_LENGTH_PASSWORD);
|
||||
}
|
||||
$this->kkt_config['prod']['pass'] = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает логин ККТ в соответствии с флагом тестового режима
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['pass'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает URL для приёма колбеков
|
||||
*
|
||||
* @param string $url
|
||||
* @return $this
|
||||
* @throws \AtolOnline\Exceptions\AtolCallbackUrlTooLongException Слишком длинный Callback URL
|
||||
* @throws \AtolOnline\Exceptions\AtolInvalidCallbackUrlException Невалидный Callback URL
|
||||
*/
|
||||
public function setCallbackUrl(string $url)
|
||||
{
|
||||
if (valid_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) {
|
||||
throw new AtolCallbackUrlTooLongException($url, Constraints::MAX_LENGTH_CALLBACK_URL);
|
||||
} elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) {
|
||||
throw new AtolInvalidCallbackUrlException('Callback URL not matches with pattern');
|
||||
}
|
||||
$this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['callback_url'] = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает URL для приёма колбеков
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCallbackUrl(): string
|
||||
{
|
||||
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['callback_url'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает последний ответ сервера
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastResponse()
|
||||
{
|
||||
return $this->last_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает флаг тестового режима
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTestMode(): bool
|
||||
{
|
||||
return $this->is_test_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает флаг тестового режима
|
||||
*
|
||||
* @param bool $test_mode
|
||||
* @return $this
|
||||
*/
|
||||
public function setTestMode(bool $test_mode = true)
|
||||
{
|
||||
$this->is_test_mode = $test_mode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ прихода
|
||||
*
|
||||
* @param \AtolOnline\Entities\Document $document Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function sell(Document $document, ?string $external_id = null)
|
||||
{
|
||||
if ($document->getCorrectionInfo()) {
|
||||
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
|
||||
}
|
||||
return $this->registerDocument('sell', 'receipt', $document, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ возврата прихода
|
||||
*
|
||||
* @param \AtolOnline\Entities\Document $document Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function sellRefund(Document $document, ?string $external_id = null)
|
||||
{
|
||||
if ($document->getCorrectionInfo()) {
|
||||
throw new AtolCorrectionInfoException('Invalid operation on correction document');
|
||||
}
|
||||
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ коррекции прихода
|
||||
*
|
||||
* @param \AtolOnline\Entities\Document $document Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function sellCorrection(Document $document, ?string $external_id = null)
|
||||
{
|
||||
if (!$document->getCorrectionInfo()) {
|
||||
throw new AtolCorrectionInfoException();
|
||||
}
|
||||
$document->setClient(null)->setItems([]);
|
||||
return $this->registerDocument('sell_correction', 'correction', $document, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ расхода
|
||||
*
|
||||
* @param \AtolOnline\Entities\Document $document
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function buy(Document $document, ?string $external_id = null)
|
||||
{
|
||||
if ($document->getCorrectionInfo()) {
|
||||
throw new AtolCorrectionInfoException('Invalid operation on correction document');
|
||||
}
|
||||
return $this->registerDocument('buy', 'receipt', $document, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ возврата расхода
|
||||
*
|
||||
* @param \AtolOnline\Entities\Document $document
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function buyRefund(Document $document, ?string $external_id = null)
|
||||
{
|
||||
if ($document->getCorrectionInfo()) {
|
||||
throw new AtolCorrectionInfoException('Invalid operation on correction document');
|
||||
}
|
||||
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует документ коррекции расхода
|
||||
*
|
||||
* @param \AtolOnline\Entities\Document $document
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длтина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function buyCorrection(Document $document, ?string $external_id = null)
|
||||
{
|
||||
if (!$document->getCorrectionInfo()) {
|
||||
throw new AtolCorrectionInfoException();
|
||||
}
|
||||
$document->setClient(null)->setItems([]);
|
||||
return $this->registerDocument('buy_correction', 'correction', $document, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет статус чека на ККТ один раз
|
||||
*
|
||||
* @param string $uuid UUID регистрации
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolInvalidUuidException Некорректный UUID документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function getDocumentStatus(string $uuid)
|
||||
{
|
||||
$uuid = trim($uuid);
|
||||
if (!Uuid::isValid($uuid)) {
|
||||
throw new AtolInvalidUuidException($uuid);
|
||||
}
|
||||
$this->auth();
|
||||
return $this->sendAtolRequest('GET', 'report/'.$uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет статус чека на ККТ нужное количество раз с указанным интервалом.
|
||||
* Вернёт результат как только при очередной проверке сменится статус регистрации документа.
|
||||
*
|
||||
* @param string $uuid UUID регистрации
|
||||
* @param int $retry_count Количество попыток
|
||||
* @param int $timeout Таймаут в секундах между попытками
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolInvalidUuidException Некорректный UUID документа
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1)
|
||||
{
|
||||
$try = 0;
|
||||
do {
|
||||
$response = $this->getDocumentStatus($uuid);
|
||||
if ($response->isValid() && $response->getContent()->status == 'done') {
|
||||
break;
|
||||
} else {
|
||||
sleep($timeout);
|
||||
}
|
||||
++$try;
|
||||
} while ($try < $retry_count);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает текущий токен авторизации
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthToken(): ?string
|
||||
{
|
||||
return $this->auth_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает заранее известный токен авторизации
|
||||
*
|
||||
* @param string|null $auth_token
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthToken(?string $auth_token)
|
||||
{
|
||||
$this->auth_token = $auth_token;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сбрасывает настройки ККТ по умолчанию
|
||||
*/
|
||||
protected function resetKktConfig(): void
|
||||
{
|
||||
$this->kkt_config['prod']['group'] = '';
|
||||
$this->kkt_config['prod']['login'] = '';
|
||||
$this->kkt_config['prod']['pass'] = '';
|
||||
$this->kkt_config['prod']['url'] = 'https://online.atol.ru/possystem/v4';
|
||||
$this->kkt_config['prod']['callback_url'] = '';
|
||||
$this->kkt_config['test']['group'] = TestEnvParams::GROUP;
|
||||
$this->kkt_config['test']['login'] = TestEnvParams::LOGIN;
|
||||
$this->kkt_config['test']['pass'] = TestEnvParams::PASSWORD;
|
||||
$this->kkt_config['test']['url'] = 'https://testonline.atol.ru/possystem/v4';
|
||||
$this->kkt_config['test']['callback_url'] = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает набор заголовков для HTTP-запроса
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeaders()
|
||||
{
|
||||
$headers['Content-type'] = 'application/json; charset=utf-8';
|
||||
if ($this->getAuthToken()) {
|
||||
$headers['Token'] = $this->getAuthToken();
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает адрес сервера в соответствии с флагом тестового режима
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEndpoint(): string
|
||||
{
|
||||
return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['url'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает полный URL до метода API
|
||||
*
|
||||
* @param string $to_method
|
||||
* @param array|null $get_parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function makeUrl(string $to_method, array $get_parameters = null)
|
||||
{
|
||||
$url = $this->getEndpoint().($this->getAuthToken() ? '/'.$this->getGroup() : '').'/'.$to_method;
|
||||
if ($get_parameters && is_array($get_parameters)) {
|
||||
$url .= '?'.http_build_query($get_parameters);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Делает запрос, возвращает декодированный ответ
|
||||
*
|
||||
* @param string $http_method Метод HTTP (GET, POST и пр)
|
||||
* @param string $api_method Метод API
|
||||
* @param mixed $data Данные для передачи
|
||||
* @param array|null $options Параметры Guzzle
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
|
||||
*/
|
||||
protected function sendAtolRequest(string $http_method, string $api_method, $data = null, array $options = null)
|
||||
{
|
||||
$http_method = strtoupper($http_method);
|
||||
$options['headers'] = $this->getHeaders();
|
||||
$url = $http_method == 'GET'
|
||||
? $this->makeUrl($api_method, $data)
|
||||
: $this->makeUrl($api_method, ['token' => $this->getAuthToken()]);
|
||||
if ($http_method != 'GET') {
|
||||
$options['json'] = $data;
|
||||
}
|
||||
$response = $this->request($http_method, $url, $options);
|
||||
return $this->last_response = new KktResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Производит авторизацию на ККТ и получает токен доступа для дальнейших HTTP-запросов
|
||||
*
|
||||
* @return bool
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
protected function auth()
|
||||
{
|
||||
if (!$this->getAuthToken()) {
|
||||
$result = $this->sendAtolRequest('GET', 'getToken', [
|
||||
'login' => $this->getLogin(),
|
||||
'pass' => $this->getPassword(),
|
||||
]);
|
||||
if (!$result->isValid() || !$result->getContent()->token) {
|
||||
throw new AtolAuthFailedException($result);
|
||||
}
|
||||
$this->auth_token = $result->getContent()->token;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправляет документ на регистрацию
|
||||
*
|
||||
* @param string $api_method Метод API
|
||||
* @param string $type Тип документа: receipt, correction
|
||||
* @param \AtolOnline\Entities\Document $document Объект документа
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
|
||||
* @return \AtolOnline\Api\KktResponse
|
||||
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
|
||||
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
protected function registerDocument(string $api_method, string $type, Document $document, ?string $external_id = null)
|
||||
{
|
||||
$type = trim($type);
|
||||
if (!in_array($type, ['receipt', 'correction'])) {
|
||||
throw new AtolWrongDocumentTypeException($type);
|
||||
}
|
||||
$this->auth();
|
||||
if ($this->isTestMode()) {
|
||||
$document->setCompany(($document->getCompany() ?: new Company())
|
||||
->setInn(TestEnvParams::INN)
|
||||
->setSno(TestEnvParams::SNO)
|
||||
->setPaymentAddress(TestEnvParams::PAYMENT_ADDRESS));
|
||||
}
|
||||
$data['timestamp'] = date('d.m.y H:i:s');
|
||||
$data['external_id'] = $external_id ?: Uuid::uuid4()->toString();
|
||||
$data[$type] = $document;
|
||||
if ($this->getCallbackUrl()) {
|
||||
$data['service'] = ['callback_url' => $this->getCallbackUrl()];
|
||||
}
|
||||
return $this->sendAtolRequest('POST', trim($api_method), $data);
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace AtolOnline\Constants;
|
||||
|
||||
/**
|
||||
* Класс с константами ограничений: максимальные длины, правила валидации значений
|
||||
*
|
||||
* @package AtolOnline\Constants
|
||||
*/
|
||||
class Constraints
|
||||
{
|
||||
/**
|
||||
* Максимальная длина Callback URL
|
||||
*/
|
||||
const MAX_LENGTH_CALLBACK_URL = 256;
|
||||
|
||||
/**
|
||||
* Максимальная длина email
|
||||
*/
|
||||
const MAX_LENGTH_EMAIL = 64;
|
||||
|
||||
/**
|
||||
* Максимальная длина логина ККТ
|
||||
*/
|
||||
const MAX_LENGTH_LOGIN = 100;
|
||||
|
||||
/**
|
||||
* Максимальная длина пароля ККТ
|
||||
*/
|
||||
const MAX_LENGTH_PASSWORD = 100;
|
||||
|
||||
/**
|
||||
* Максимальная длина имени покупателя
|
||||
*/
|
||||
const MAX_LENGTH_CLIENT_NAME = 256;
|
||||
|
||||
/**
|
||||
* Максимальная длина телефона покупателя
|
||||
*/
|
||||
const MAX_LENGTH_CLIENT_PHONE = 64;
|
||||
|
||||
/**
|
||||
* Максимальная длина адреса места расчётов
|
||||
*/
|
||||
const MAX_LENGTH_PAYMENT_ADDRESS = 256;
|
||||
|
||||
/**
|
||||
* Максимальная длина имени кассира
|
||||
*/
|
||||
const MAX_LENGTH_CASHIER_NAME = 64;
|
||||
|
||||
/**
|
||||
* Максимальная длина наименования предмета расчётов
|
||||
*/
|
||||
const MAX_LENGTH_ITEM_NAME = 128;
|
||||
|
||||
/**
|
||||
* Максимальная длина единицы измерения предмета расчётов
|
||||
*/
|
||||
const MAX_LENGTH_MEASUREMENT_UNIT = 16;
|
||||
|
||||
/**
|
||||
* Максимальная длина пользовательских данных для предмета расчётов
|
||||
*/
|
||||
const MAX_LENGTH_USER_DATA = 64;
|
||||
|
||||
/**
|
||||
* Регулярное выражание для валидации строки ИНН
|
||||
*/
|
||||
const PATTERN_INN = "/(^[0-9]{10}$)|(^[0-9]{12}$)/";
|
||||
|
||||
/**
|
||||
* Регулярное выражание для валидации строки Callback URL
|
||||
*/
|
||||
const PATTERN_CALLBACK_URL = "/^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/";
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Constants;
|
||||
|
||||
/**
|
||||
* Константы, определяющие параметры тестовой среды
|
||||
*
|
||||
* @see https://online.atol.ru/files/ffd/test_sreda.txt
|
||||
* @package AtolOnline\Constants
|
||||
*/
|
||||
class TestEnvParams
|
||||
{
|
||||
/**
|
||||
* Логин
|
||||
*/
|
||||
const LOGIN = 'v4-online-atol-ru';
|
||||
|
||||
/**
|
||||
* Пароль
|
||||
*/
|
||||
const PASSWORD = 'iGFFuihss';
|
||||
|
||||
/**
|
||||
* Группа
|
||||
*/
|
||||
const GROUP = 'v4-online-atol-ru_4179';
|
||||
|
||||
/**
|
||||
* Система налогообложения
|
||||
*/
|
||||
const SNO = SnoTypes::OSN;
|
||||
|
||||
/**
|
||||
* ИНН
|
||||
*/
|
||||
const INN = '5544332219';
|
||||
|
||||
/**
|
||||
* Адрес места расчётов
|
||||
*/
|
||||
const PAYMENT_ADDRESS = 'https://v4.online.atol.ru';
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\{Constants\Constraints, Exceptions\AtolNameTooLongException, Exceptions\AtolPhoneTooLongException, Traits\HasEmail, Traits\HasInn};
|
||||
|
||||
/**
|
||||
* Класс Client, описывающий сущность покупателя
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class Client extends Entity
|
||||
{
|
||||
use
|
||||
/**
|
||||
* Покупатель может иметь почту. Тег ФФД - 1008.
|
||||
*/
|
||||
HasEmail,
|
||||
|
||||
/**
|
||||
* Покупатель может иметь ИНН. Тег ФФД - 1228.
|
||||
*/
|
||||
HasInn;
|
||||
|
||||
/**
|
||||
* @var string Телефон покупателя. Тег ФФД - 1008.
|
||||
*/
|
||||
protected $phone;
|
||||
|
||||
/**
|
||||
* @var string Имя покупателя. Тег ФФД - 1227.
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Client constructor.
|
||||
*
|
||||
* @param string|null $name Наименование
|
||||
* @param string|null $phone Телефон
|
||||
* @param string|null $email Email
|
||||
* @param string|null $inn ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException Слишком длинный email
|
||||
* @throws \AtolOnline\Exceptions\AtolEmailValidateException Невалидный email
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws \AtolOnline\Exceptions\AtolNameTooLongException Слишком длинное имя
|
||||
* @throws \AtolOnline\Exceptions\AtolPhoneTooLongException СЛишком длинный номер телефона
|
||||
*/
|
||||
public function __construct(?string $name = null, ?string $phone = null, ?string $email = null, ?string $inn = null)
|
||||
{
|
||||
if ($name) {
|
||||
$this->setName($name);
|
||||
}
|
||||
if ($email) {
|
||||
$this->setEmail($email);
|
||||
}
|
||||
if ($phone) {
|
||||
$this->setPhone($phone);
|
||||
}
|
||||
if ($inn) {
|
||||
$this->setInn($inn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает имя покупателя. Тег ФФД - 1227.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает имя покупателя
|
||||
* Тег ФФД - 1227.
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
* @throws AtolNameTooLongException
|
||||
*/
|
||||
public function setName(string $name)
|
||||
{
|
||||
$name = trim($name);
|
||||
if (valid_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) {
|
||||
throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_CLIENT_NAME);
|
||||
}
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает телефон покупателя.
|
||||
* Тег ФФД - 1008.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPhone()
|
||||
{
|
||||
return $this->phone ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает телефон покупателя.
|
||||
* Тег ФФД - 1008.
|
||||
* Входная строка лишается всех знаков, кроме цифр и знака '+'.
|
||||
*
|
||||
* @param string $phone
|
||||
* @return $this
|
||||
* @throws AtolPhoneTooLongException
|
||||
*/
|
||||
public function setPhone(string $phone)
|
||||
{
|
||||
$phone = preg_replace("/[^0-9+]/", '', $phone);
|
||||
if (valid_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_PHONE) {
|
||||
throw new AtolPhoneTooLongException($phone, Constraints::MAX_LENGTH_CLIENT_PHONE);
|
||||
}
|
||||
$this->phone = $phone;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$json = [];
|
||||
if ($this->getName()) {
|
||||
$json['name'] = $this->getName() ?? '';
|
||||
}
|
||||
if ($this->getEmail()) {
|
||||
$json['email'] = $this->getEmail() ?? '';
|
||||
}
|
||||
if ($this->getPhone()) {
|
||||
$json['phone'] = $this->getPhone() ?? '';
|
||||
}
|
||||
if ($this->getInn()) {
|
||||
$json['inn'] = $this->getInn() ?? '';
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\{Constants\Constraints,
|
||||
Exceptions\AtolEmailTooLongException,
|
||||
Exceptions\AtolEmailValidateException,
|
||||
Exceptions\AtolInnWrongLengthException,
|
||||
Exceptions\AtolPaymentAddressTooLongException,
|
||||
Traits\HasEmail,
|
||||
Traits\HasInn
|
||||
};
|
||||
|
||||
/**
|
||||
* Класс, описывающий сущность компании-продавца
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class Company extends Entity
|
||||
{
|
||||
use
|
||||
/**
|
||||
* Продавец должен иметь почту. Тег ФФД - 1117.
|
||||
*/
|
||||
HasEmail,
|
||||
|
||||
/**
|
||||
* Продавец должен иметь ИНН. Тег ФФД - 1018.
|
||||
*/
|
||||
HasInn;
|
||||
|
||||
/**
|
||||
* @var string Система налогообложения продавца. Тег ФФД - 1055.
|
||||
*/
|
||||
protected $sno;
|
||||
|
||||
/**
|
||||
* @var string Место расчётов (адрес интернет-магазина). Тег ФФД - 1187.
|
||||
*/
|
||||
protected $payment_address;
|
||||
|
||||
/**
|
||||
* Company constructor.
|
||||
*
|
||||
* @param string|null $sno
|
||||
* @param string|null $inn
|
||||
* @param string|null $paymentAddress
|
||||
* @param string|null $email
|
||||
* @throws AtolEmailTooLongException Слишком длинный email
|
||||
* @throws AtolEmailValidateException Невалидный email
|
||||
* @throws AtolInnWrongLengthException Некорректная длина ИНН
|
||||
* @throws AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
*/
|
||||
public function __construct(string $sno = null, string $inn = null, string $paymentAddress = null, string $email = null)
|
||||
{
|
||||
if ($sno) {
|
||||
$this->setSno($sno);
|
||||
}
|
||||
if ($inn) {
|
||||
$this->setInn($inn);
|
||||
}
|
||||
if ($paymentAddress) {
|
||||
$this->setPaymentAddress($paymentAddress);
|
||||
}
|
||||
if ($email) {
|
||||
$this->setEmail($email);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный тип налогообложения. Тег ФФД - 1055.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSno()
|
||||
{
|
||||
return $this->sno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип налогообложения. Тег ФФД - 1055.
|
||||
*
|
||||
* @param string $sno
|
||||
* @return $this
|
||||
*/
|
||||
public function setSno(string $sno)
|
||||
{
|
||||
$this->sno = trim($sno);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный адрес места расчётов. Тег ФФД - 1187.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentAddress()
|
||||
{
|
||||
return $this->payment_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает адрес места расчётов. Тег ФФД - 1187.
|
||||
*
|
||||
* @param string $payment_address
|
||||
* @return $this
|
||||
* @throws AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
|
||||
*/
|
||||
public function setPaymentAddress(string $payment_address)
|
||||
{
|
||||
$payment_address = trim($payment_address);
|
||||
if (valid_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) {
|
||||
throw new AtolPaymentAddressTooLongException($payment_address, Constraints::MAX_LENGTH_PAYMENT_ADDRESS);
|
||||
}
|
||||
$this->payment_address = $payment_address;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'email' => $this->getEmail(),
|
||||
'sno' => $this->getSno(),
|
||||
'inn' => $this->getInn(),
|
||||
'payment_address' => $this->getPaymentAddress(),
|
||||
];
|
||||
}
|
||||
}
|
@ -1,171 +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;
|
||||
|
||||
/**
|
||||
* Класс CorrectionInfo, описывающий данные коррекции
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class CorrectionInfo extends Entity
|
||||
{
|
||||
/**
|
||||
* @var int Тип коррекции. Тег ФФД - 1173.
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var string Дата документа основания для коррекции. Тег ФФД - 1178.
|
||||
*/
|
||||
protected $base_date;
|
||||
|
||||
/**
|
||||
* @var string Номер документа основания для коррекции. Тег ФФД - 1179.
|
||||
*/
|
||||
protected $base_number;
|
||||
|
||||
/**
|
||||
* @var string Описание коррекции. Тег ФФД - 1177.
|
||||
*/
|
||||
protected $base_name;
|
||||
|
||||
/**
|
||||
* CorrectionInfo constructor.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает номер документа основания для коррекции.
|
||||
* Тег ФФД - 1179.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getNumber(): ?string
|
||||
{
|
||||
return $this->base_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает номер документа основания для коррекции.
|
||||
* Тег ФФД - 1179.
|
||||
*
|
||||
* @param string $number
|
||||
* @return $this
|
||||
*/
|
||||
public function setNumber(string $number)
|
||||
{
|
||||
$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.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDate(): ?string
|
||||
{
|
||||
return $this->base_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает дату документа основания для коррекции.
|
||||
* Тег ФФД - 1178.
|
||||
*
|
||||
* @param string $date Строка в формате d.m.Y
|
||||
* @return $this
|
||||
*/
|
||||
public function setDate(string $date)
|
||||
{
|
||||
$this->base_date = $date;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает тип коррекции.
|
||||
* Тег ФФД - 1173.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип коррекции.
|
||||
* Тег ФФД - 1173.
|
||||
*
|
||||
* @param string $type
|
||||
* @return $this
|
||||
*/
|
||||
public function setType(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'type' => $this->getType() ?? '', // обязателен
|
||||
'base_date' => $this->getDate() ?? '', // обязателен
|
||||
'base_number' => $this->getNumber() ?? '', // обязателен
|
||||
'base_name' => $this->getName() ?? '' // не обязателен
|
||||
];
|
||||
}
|
||||
}
|
@ -1,452 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Exceptions\AtolCashierTooLongException;
|
||||
use AtolOnline\Exceptions\AtolException;
|
||||
use AtolOnline\Exceptions\AtolInvalidJsonException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий документ
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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([]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет новую ставку НДС в массив ставок НДС
|
||||
*
|
||||
* @param \AtolOnline\Entities\Vat $vat Объект ставки НДС
|
||||
* @return $this
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
|
||||
*/
|
||||
public function addVat(Vat $vat)
|
||||
{
|
||||
$this->vats->add($vat);
|
||||
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);
|
||||
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|null
|
||||
*/
|
||||
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|null
|
||||
*/
|
||||
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)
|
||||
{
|
||||
if ($cashier !== null) {
|
||||
$cashier = trim($cashier);
|
||||
if (valid_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
|
||||
throw new AtolCashierTooLongException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME);
|
||||
}
|
||||
}
|
||||
$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;
|
||||
$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 \AtolOnline\Entities\Document
|
||||
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException
|
||||
* @throws \AtolOnline\Exceptions\AtolEmailValidateException
|
||||
* @throws \AtolOnline\Exceptions\AtolException
|
||||
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException
|
||||
* @throws \AtolOnline\Exceptions\AtolInvalidJsonException
|
||||
* @throws \AtolOnline\Exceptions\AtolNameTooLongException
|
||||
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException
|
||||
* @throws \AtolOnline\Exceptions\AtolPhoneTooLongException
|
||||
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyException
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException
|
||||
* @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException
|
||||
* @throws \AtolOnline\Exceptions\AtolUnitTooLongException
|
||||
* @throws \AtolOnline\Exceptions\AtolUserdataTooLongException
|
||||
*/
|
||||
public static function fromRaw(string $json)
|
||||
{
|
||||
$array = json_decode($json, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new AtolInvalidJsonException();
|
||||
}
|
||||
$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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,28 +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 JsonSerializable;
|
||||
|
||||
/**
|
||||
* Абстрактное описание любой сущности, представляемой как JSON
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
abstract class Entity implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
@ -1,397 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\{Constants\Constraints,
|
||||
Exceptions\AtolNameTooLongException,
|
||||
Exceptions\AtolPriceTooHighException,
|
||||
Exceptions\AtolTooManyException,
|
||||
Exceptions\AtolUnitTooLongException,
|
||||
Exceptions\AtolUserdataTooLongException,
|
||||
Traits\RublesKopeksConverter
|
||||
};
|
||||
|
||||
/**
|
||||
* Предмет расчёта (товар, услуга)
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class Item extends Entity
|
||||
{
|
||||
use RublesKopeksConverter;
|
||||
|
||||
/**
|
||||
* @var string Наименование. Тег ФФД - 1030.
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var int Цена в копейках (с учётом скидок и наценок). Тег ФФД - 1079.
|
||||
*/
|
||||
protected $price = 0;
|
||||
|
||||
/**
|
||||
* @var float Количество, вес. Тег ФФД - 1023.
|
||||
*/
|
||||
protected $quantity = 0.0;
|
||||
|
||||
/**
|
||||
* @var float Сумма в копейках. Тег ФФД - 1043.
|
||||
*/
|
||||
protected $sum = 0;
|
||||
|
||||
/**
|
||||
* @var string Единица измерения количества. Тег ФФД - 1197.
|
||||
*/
|
||||
protected $measurement_unit;
|
||||
|
||||
/**
|
||||
* @var Vat Ставка НДС
|
||||
*/
|
||||
protected $vat;
|
||||
|
||||
/**
|
||||
* @var string Признак способа расчёта. Тег ФФД - 1214.
|
||||
*/
|
||||
protected $payment_method;
|
||||
|
||||
/**
|
||||
* @var string Признак объекта расчёта. Тег ФФД - 1212.
|
||||
*/
|
||||
protected $payment_object;
|
||||
|
||||
/**
|
||||
* @var string Дополнительный реквизит. Тег ФФД - 1191.
|
||||
*/
|
||||
protected $user_data;
|
||||
|
||||
/**
|
||||
* Item constructor.
|
||||
*
|
||||
* @param string|null $name Наименование
|
||||
* @param float|null $price Цена за одну единицу
|
||||
* @param float|null $quantity Количество
|
||||
* @param string|null $measurement_unit Единица измерения
|
||||
* @param string|null $vat_type Ставка НДС
|
||||
* @param string|null $payment_object Признак
|
||||
* @param string|null $payment_method Способ расчёта
|
||||
* @throws AtolNameTooLongException Слишком длинное наименование
|
||||
* @throws AtolPriceTooHighException Слишком высокая цена за одну единицу
|
||||
* @throws AtolTooManyException Слишком большое количество
|
||||
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?float $price = null,
|
||||
?float $quantity = null,
|
||||
?string $measurement_unit = null,
|
||||
$vat_type = null,
|
||||
?string $payment_object = null,
|
||||
?string $payment_method = null
|
||||
) {
|
||||
if ($name) {
|
||||
$this->setName($name);
|
||||
}
|
||||
if ($price) {
|
||||
$this->setPrice($price);
|
||||
}
|
||||
if ($quantity) {
|
||||
$this->setQuantity($quantity);
|
||||
}
|
||||
if ($measurement_unit) {
|
||||
$this->setMeasurementUnit($measurement_unit);
|
||||
}
|
||||
if ($vat_type) {
|
||||
$this->setVatType($vat_type);
|
||||
}
|
||||
if ($payment_object) {
|
||||
$this->setPaymentObject($payment_object);
|
||||
}
|
||||
if ($payment_method) {
|
||||
$this->setPaymentMethod($payment_method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает наименование. Тег ФФД - 1030.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устаналивает наименование. Тег ФФД - 1030.
|
||||
*
|
||||
* @param string $name Наименование
|
||||
* @return $this
|
||||
* @throws AtolNameTooLongException Слишком длинное имя/наименование
|
||||
*/
|
||||
public function setName(string $name)
|
||||
{
|
||||
$name = trim($name);
|
||||
if (valid_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) {
|
||||
throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_ITEM_NAME);
|
||||
}
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает цену в рублях. Тег ФФД - 1079.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getPrice()
|
||||
{
|
||||
return self::toRub($this->price);
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает цену в рублях. Тег ФФД - 1079.
|
||||
*
|
||||
* @param float $rubles Цена за одну единицу в рублях
|
||||
* @return $this
|
||||
* @throws AtolPriceTooHighException Слишком высокая цена за одну единицу
|
||||
*/
|
||||
public function setPrice(float $rubles)
|
||||
{
|
||||
if ($rubles > 42949672.95) {
|
||||
throw new AtolPriceTooHighException($rubles, 42949672.95);
|
||||
}
|
||||
$this->price = self::toKop($rubles);
|
||||
$this->calcSum();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает количество. Тег ФФД - 1023.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getQuantity(): float
|
||||
{
|
||||
return $this->quantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает количество. Тег ФФД - 1023.
|
||||
*
|
||||
* @param float $quantity Количество
|
||||
* @param string|null $measurement_unit Единица измерения количества
|
||||
* @return $this
|
||||
* @throws AtolTooManyException Слишком большое количество
|
||||
* @throws AtolPriceTooHighException Слишком высокая общая стоимость
|
||||
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
|
||||
*/
|
||||
public function setQuantity(float $quantity, string $measurement_unit = null)
|
||||
{
|
||||
$quantity = round($quantity, 3);
|
||||
if ($quantity > 99999.999) {
|
||||
throw new AtolTooManyException($quantity, 99999.999);
|
||||
}
|
||||
$this->quantity = $quantity;
|
||||
$this->calcSum();
|
||||
if ($measurement_unit) {
|
||||
$this->setMeasurementUnit($measurement_unit);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает заданную единицу измерения количества. Тег ФФД - 1197.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMeasurementUnit(): string
|
||||
{
|
||||
return $this->measurement_unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает единицу измерения количества. Тег ФФД - 1197.
|
||||
*
|
||||
* @param string $measurement_unit Единица измерения количества
|
||||
* @return $this
|
||||
* @throws AtolUnitTooLongException Слишком длинное название единицы измерения
|
||||
*/
|
||||
public function setMeasurementUnit(string $measurement_unit)
|
||||
{
|
||||
$measurement_unit = trim($measurement_unit);
|
||||
if (valid_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) {
|
||||
throw new AtolUnitTooLongException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT);
|
||||
}
|
||||
$this->measurement_unit = $measurement_unit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает признак способа оплаты. Тег ФФД - 1214.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentMethod(): string
|
||||
{
|
||||
return $this->payment_method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает признак способа оплаты. Тег ФФД - 1214.
|
||||
*
|
||||
* @param string $payment_method Признак способа оплаты
|
||||
* @return $this
|
||||
* @todo Проверка допустимых значений
|
||||
*/
|
||||
public function setPaymentMethod(string $payment_method)
|
||||
{
|
||||
$this->payment_method = trim($payment_method);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает признак предмета расчёта. Тег ФФД - 1212.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentObject(): string
|
||||
{
|
||||
return $this->payment_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает признак предмета расчёта. Тег ФФД - 1212.
|
||||
*
|
||||
* @param string $payment_object Признак предмета расчёта
|
||||
* @return $this
|
||||
* @todo Проверка допустимых значений
|
||||
*/
|
||||
public function setPaymentObject(string $payment_object)
|
||||
{
|
||||
$this->payment_object = trim($payment_object);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает ставку НДС
|
||||
*
|
||||
* @return \AtolOnline\Entities\Vat|null
|
||||
*/
|
||||
public function getVat(): ?Vat
|
||||
{
|
||||
return $this->vat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает ставку НДС
|
||||
*
|
||||
* @param string|null $vat_type Тип ставки НДС. Передать null, чтобы удалить ставку.
|
||||
* @return $this
|
||||
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException
|
||||
*/
|
||||
public function setVatType(?string $vat_type)
|
||||
{
|
||||
if ($vat_type) {
|
||||
$this->vat
|
||||
? $this->vat->setType($vat_type)
|
||||
: $this->vat = new Vat($vat_type);
|
||||
} else {
|
||||
$this->vat = null;
|
||||
}
|
||||
$this->calcSum();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает дополнительный реквизит. Тег ФФД - 1191.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUserData(): ?string
|
||||
{
|
||||
return $this->user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает дополнительный реквизит. Тег ФФД - 1191.
|
||||
*
|
||||
* @param string $user_data Дополнительный реквизит. Тег ФФД - 1191.
|
||||
* @return $this
|
||||
* @throws AtolUserdataTooLongException Слишком длинный дополнительный реквизит
|
||||
*/
|
||||
public function setUserData(string $user_data)
|
||||
{
|
||||
$user_data = trim($user_data);
|
||||
if (valid_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) {
|
||||
throw new AtolUserdataTooLongException($user_data, Constraints::MAX_LENGTH_USER_DATA);
|
||||
}
|
||||
$this->user_data = $user_data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает стоимость. Тег ФФД - 1043.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSum(): float
|
||||
{
|
||||
return self::toRub($this->sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчитывает стоимость и размер НДС на неё
|
||||
*
|
||||
* @return float
|
||||
* @throws AtolPriceTooHighException Слишком большая сумма
|
||||
*/
|
||||
public function calcSum()
|
||||
{
|
||||
$sum = $this->quantity * $this->price;
|
||||
if (self::toRub($sum) > 42949672.95) {
|
||||
throw new AtolPriceTooHighException($sum, 42949672.95);
|
||||
}
|
||||
$this->sum = $sum;
|
||||
if ($this->vat) {
|
||||
$this->vat->setSum(self::toRub($sum));
|
||||
}
|
||||
return $this->getSum();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$json = [
|
||||
'name' => $this->getName(), // обязательно
|
||||
'price' => $this->getPrice(), // обязательно
|
||||
'quantity' => $this->getQuantity(), // обязательно
|
||||
'sum' => $this->getSum(), // обязательно
|
||||
'measurement_unit' => $this->getMeasurementUnit(),
|
||||
'payment_method' => $this->getPaymentMethod(),
|
||||
'payment_object' => $this->getPaymentObject()
|
||||
//TODO nomenclature_code
|
||||
//TODO agent_info
|
||||
//TODO supplier_info
|
||||
//TODO excise
|
||||
//TODO country_code
|
||||
//TODO declaration_number
|
||||
];
|
||||
if ($this->getVat()) {
|
||||
$json['vat'] = $this->getVat()->jsonSerialize();
|
||||
}
|
||||
if ($this->getUserData()) {
|
||||
$json['user_data'] = $this->getUserData();
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Exceptions\AtolTooManyItemsException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий массив предметов расчёта
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class ItemArray extends Entity
|
||||
{
|
||||
/**
|
||||
* Максимальное количество элементов в массиве
|
||||
* По документации ограничение по количеству предметов расчёта = от 1 до 100,
|
||||
* однако в схеме sell не указан receipt.properties.items.maxItems
|
||||
*/
|
||||
public const MAX_COUNT = 100;
|
||||
|
||||
/**
|
||||
* @var Item[] Массив предметов расчёта
|
||||
*/
|
||||
private $items = [];
|
||||
|
||||
/**
|
||||
* ItemArray constructor.
|
||||
*
|
||||
* @param Item[]|null $items Массив предметов расчёта
|
||||
* @throws AtolTooManyItemsException Слишком много предметов расчёта
|
||||
*/
|
||||
public function __construct(?array $items = null)
|
||||
{
|
||||
if ($items) {
|
||||
$this->set($items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает массив предметов расчёта
|
||||
*
|
||||
* @param Item[] $items Массив предметов расчёта
|
||||
* @return $this
|
||||
* @throws AtolTooManyItemsException Слишком много предметов расчёта
|
||||
*/
|
||||
public function set(array $items)
|
||||
{
|
||||
if ($this->validateCount($items)) {
|
||||
$this->items = $items;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет предмет расчёта в массив
|
||||
*
|
||||
* @param Item $item Объект предмета расчёта
|
||||
* @return $this
|
||||
* @throws AtolTooManyItemsException Слишком много предметов расчёта
|
||||
*/
|
||||
public function add(Item $item)
|
||||
{
|
||||
if ($this->validateCount()) {
|
||||
$this->items[] = $item;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает массив предметов расчёта
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->get() as $item) {
|
||||
$result[] = $item->jsonSerialize();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет количество предметов расчёта
|
||||
*
|
||||
* @param Item[]|null $items Если передать массив, то проверит количество его элементов.
|
||||
* Иначе проверит количество уже присвоенных элементов.
|
||||
* @return bool true если всё хорошо, иначе выбрасывает исключение
|
||||
* @throws AtolTooManyItemsException Слишком много предметов расчёта
|
||||
*/
|
||||
protected function validateCount(?array $items = null): bool
|
||||
{
|
||||
if ((!empty($items) && count($items) >= self::MAX_COUNT) || count($this->items) >= self::MAX_COUNT) {
|
||||
throw new AtolTooManyItemsException(count($items), self::MAX_COUNT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Constants\PaymentTypes;
|
||||
|
||||
/**
|
||||
* Класс, описывающий оплату. Тег ФФД - 1031, 1081, 1215, 1216, 1217.
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class Payment extends Entity
|
||||
{
|
||||
/**
|
||||
* @var int Тип оплаты
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var float Сумма оплаты
|
||||
*/
|
||||
protected $sum;
|
||||
|
||||
/**
|
||||
* Payment constructor.
|
||||
*
|
||||
* @param int $payment_type Тип оплаты
|
||||
* @param float $sum Сумма оплаты
|
||||
*/
|
||||
public function __construct(int $payment_type = PaymentTypes::ELECTRON, float $sum = 0.0)
|
||||
{
|
||||
$this->setType($payment_type);
|
||||
$this->setSum($sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает тип оплаты. Тег ФФД - 1031, 1081, 1215, 1216, 1217.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getType(): int
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип оплаты. Тег ФФД - 1031, 1081, 1215, 1216, 1217.
|
||||
*
|
||||
* @param int $type
|
||||
* @return $this
|
||||
*/
|
||||
public function setType(int $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает сумму оплаты
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSum(): float
|
||||
{
|
||||
return $this->sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает сумму оплаты
|
||||
*
|
||||
* @param float $sum
|
||||
* @return $this
|
||||
*/
|
||||
public function setSum(float $sum)
|
||||
{
|
||||
$this->sum = $sum;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'type' => $this->getType(),
|
||||
'sum' => $this->getSum(),
|
||||
];
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Exceptions\AtolTooManyPaymentsException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий массив оплат
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class PaymentArray extends Entity
|
||||
{
|
||||
/**
|
||||
* Максимальное количество элементов массива
|
||||
*/
|
||||
public const MAX_COUNT = 10;
|
||||
|
||||
/**
|
||||
* @var Payment[] Массив оплат
|
||||
*/
|
||||
private $payments = [];
|
||||
|
||||
/**
|
||||
* ItemArray constructor.
|
||||
*
|
||||
* @param Payment[]|null $payments Массив оплат
|
||||
* @throws AtolTooManyPaymentsException Слишком много оплат
|
||||
*/
|
||||
public function __construct(?array $payments = null)
|
||||
{
|
||||
if ($payments) {
|
||||
$this->set($payments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает массив оплат
|
||||
*
|
||||
* @param Payment[] $payments
|
||||
* @return $this
|
||||
* @throws AtolTooManyPaymentsException Слишком много оплат
|
||||
*/
|
||||
public function set(array $payments)
|
||||
{
|
||||
if ($this->validateCount($payments)) {
|
||||
$this->payments = $payments;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет новую оплату к заданным
|
||||
*
|
||||
* @param Payment $payment Объект оплаты
|
||||
* @return $this
|
||||
* @throws AtolTooManyPaymentsException Слишком много оплат
|
||||
*/
|
||||
public function add(Payment $payment)
|
||||
{
|
||||
if ($this->validateCount()) {
|
||||
$this->payments[] = $payment;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает массив оплат
|
||||
*
|
||||
* @return Payment[]
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->payments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->get() as $payment) {
|
||||
$result[] = $payment->jsonSerialize();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет количество налоговых ставок
|
||||
*
|
||||
* @param Payment[]|null $payments Если передать массив, то проверит количество его элементов.
|
||||
* Иначе проверит количество уже присвоенных элементов.
|
||||
* @return bool true если всё хорошо, иначе выбрасывает исключение
|
||||
* @throws AtolTooManyPaymentsException Слишком много оплат
|
||||
*/
|
||||
protected function validateCount(?array $payments = null): bool
|
||||
{
|
||||
if ((!empty($payments) && count($payments) >= self::MAX_COUNT) || count($this->payments) >= self::MAX_COUNT) {
|
||||
throw new AtolTooManyPaymentsException(count($payments), self::MAX_COUNT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\{Constants\VatTypes, Traits\RublesKopeksConverter};
|
||||
|
||||
/**
|
||||
* Класс, описывающий ставку НДС
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class Vat extends Entity
|
||||
{
|
||||
use RublesKopeksConverter;
|
||||
|
||||
/**
|
||||
* @var string Выбранный тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var int Сумма в копейках, от которой пересчитывается размер НДС
|
||||
*/
|
||||
private $sum_original = 0;
|
||||
|
||||
/**
|
||||
* @var int Сумма НДС в копейках
|
||||
*/
|
||||
private $sum_final = 0;
|
||||
|
||||
/**
|
||||
* Vat constructor.
|
||||
*
|
||||
* @param string $type Тип ставки НДС
|
||||
* @param float|null $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС
|
||||
*/
|
||||
public function __construct(string $type = VatTypes::NONE, float $rubles = null)
|
||||
{
|
||||
$this->type = $type;
|
||||
if ($rubles) {
|
||||
$this->setSum($rubles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает:
|
||||
* размер НДС от суммы в копейках
|
||||
*
|
||||
* @param string $type Тип ставки НДС
|
||||
* @param int $kopeks Копейки
|
||||
* @return float|int
|
||||
* @see https://nalog-nalog.ru/nds/nalogovaya_baza_nds/kak-schitat-nds-pravilno-vychislyaem-20-ot-summy-primer-algoritm/
|
||||
* @see https://glavkniga.ru/situations/k500734
|
||||
* @see https://www.b-kontur.ru/nds-kalkuljator-online
|
||||
*/
|
||||
protected static function calculator(string $type, int $kopeks)
|
||||
{
|
||||
switch ($type) {
|
||||
case VatTypes::NONE:
|
||||
case VatTypes::VAT0:
|
||||
return 0;
|
||||
case VatTypes::VAT10:
|
||||
//return $kopeks * 10 / 100;
|
||||
case VatTypes::VAT110:
|
||||
return $kopeks * 10 / 110;
|
||||
case VatTypes::VAT18:
|
||||
//return $kopeks * 18 / 100;
|
||||
case VatTypes::VAT118:
|
||||
return $kopeks * 18 / 118;
|
||||
case VatTypes::VAT20:
|
||||
//return $kopeks * 20 / 100;
|
||||
case VatTypes::VAT120:
|
||||
return $kopeks * 20 / 120;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106.
|
||||
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
|
||||
*
|
||||
* @param string $type Тип ставки НДС
|
||||
* @return $this
|
||||
*/
|
||||
public function setType(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->setFinal();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает расчитанный итоговый размер ставки НДС в рублях. Тег ФФД - 1200.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getFinalSum()
|
||||
{
|
||||
return self::toRub($this->sum_final);
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает исходную сумму, от которой будет расчитываться итоговый размер НДС.
|
||||
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
|
||||
*
|
||||
* @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС
|
||||
* @return $this
|
||||
*/
|
||||
public function setSum(float $rubles)
|
||||
{
|
||||
$this->sum_original = self::toKop($rubles);
|
||||
$this->setFinal();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает исходную сумму, от которой расчитывается размер налога
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSum(): float
|
||||
{
|
||||
return self::toRub($this->sum_original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Прибавляет указанную сумму к общей исходной сумме.
|
||||
* Автоматически пересчитывает итоговый размер НДС от новой исходной суммы.
|
||||
*
|
||||
* @param float $rubles
|
||||
* @return $this
|
||||
*/
|
||||
public function addSum(float $rubles)
|
||||
{
|
||||
$this->sum_original += self::toKop($rubles);
|
||||
$this->setFinal();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчитывает и возвращает размер НДС от указанной суммы в рублях.
|
||||
* Не изменяет итоговый размер НДС.
|
||||
*
|
||||
* @param float|null $rubles
|
||||
* @return float
|
||||
*/
|
||||
public function calc(float $rubles): float
|
||||
{
|
||||
return self::toRub(self::calculator($this->type, self::toKop($rubles)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'type' => $this->getType(),
|
||||
'sum' => $this->getFinalSum(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчитывает и устанавливает итоговый размер ставки от исходной суммы в копейках
|
||||
*/
|
||||
protected function setFinal()
|
||||
{
|
||||
$this->sum_final = self::calculator($this->type, $this->sum_original);
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Exceptions\AtolTooManyVatsException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий массив ставок НДС
|
||||
*
|
||||
* @package AtolOnline\Entities
|
||||
*/
|
||||
class VatArray extends Entity
|
||||
{
|
||||
/**
|
||||
* Максимальное количество элементов массива
|
||||
*/
|
||||
public const MAX_COUNT = 6;
|
||||
|
||||
/**
|
||||
* @var Vat[] Массив ставок НДС
|
||||
*/
|
||||
private $vats = [];
|
||||
|
||||
/**
|
||||
* VatArray constructor.
|
||||
*
|
||||
* @param Vat[]|null $vats Массив ставок НДС
|
||||
* @throws AtolTooManyVatsException Слишком много ставок НДС
|
||||
*/
|
||||
public function __construct(?array $vats = null)
|
||||
{
|
||||
if ($vats) {
|
||||
$this->set($vats);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает массив ставок НДС
|
||||
*
|
||||
* @param Vat[] $vats Массив ставок НДС
|
||||
* @return $this
|
||||
* @throws AtolTooManyVatsException Слишком много ставок НДС
|
||||
*/
|
||||
public function set(array $vats)
|
||||
{
|
||||
if ($this->validateCount($vats)) {
|
||||
$this->vats = $vats;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет новую ставку НДС в массив
|
||||
*
|
||||
* @param Vat $vat Объект ставки НДС
|
||||
* @return $this
|
||||
* @throws AtolTooManyVatsException Слишком много ставок НДС
|
||||
*/
|
||||
public function add(Vat $vat)
|
||||
{
|
||||
if ($this->validateCount()) {
|
||||
if (isset($this->vats[$vat->getType()])) {
|
||||
$this->vats[$vat->getType()]->addSum($vat->getSum());
|
||||
} else {
|
||||
$this->vats[$vat->getType()] = $vat;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает массив ставок НДС
|
||||
*
|
||||
* @return Vat[]
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->vats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->get() as $vat) {
|
||||
$result[] = $vat->jsonSerialize();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет количество налоговых ставок
|
||||
*
|
||||
* @param Vat[]|null $vats Если передать массив, то проверит количество его элементов.
|
||||
* Иначе проверит количество уже присвоенных элементов.
|
||||
* @return bool true если всё хорошо, иначе выбрасывает исключение
|
||||
* @throws AtolTooManyVatsException Слишком много ставок НДС
|
||||
*/
|
||||
protected function validateCount(?array $vats = null): bool
|
||||
{
|
||||
if ((!empty($vats) && count($vats) >= self::MAX_COUNT) || count($this->vats) >= self::MAX_COUNT) {
|
||||
throw new AtolTooManyVatsException(count($vats), self::MAX_COUNT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use AtolOnline\Api\KktResponse;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при работе с АТОЛ Онлайн
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolAuthFailedException extends Exception
|
||||
{
|
||||
/**
|
||||
* AtolAuthFailedException constructor.
|
||||
*
|
||||
* @param \AtolOnline\Api\KktResponse $last_response
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param \Throwable|null $previous
|
||||
*/
|
||||
public function __construct(KktResponse $last_response, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
$message = $last_response->isValid()
|
||||
? $message
|
||||
: '['.$last_response->error->code.'] '.$last_response->error->text.
|
||||
'. ERROR_ID: '.$last_response->error->error_id.
|
||||
'. TYPE: '.$last_response->error->type;
|
||||
$code = $last_response->isValid() ? $code : $last_response->error->code;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинное имя кассира
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolCashierTooLongException extends AtolTooLongException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1021,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Cashier name is too long';
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке зарегистрировать документ без данных коррекции
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolCorrectionInfoException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Document must have correction info';
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать пустой email
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolEmailEmptyException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1008,
|
||||
1117,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Email cannot be empty';
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинный email
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolEmailTooLongException extends AtolTooLongException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1008,
|
||||
1117,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Email is too long';
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при ошибке валидации email
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolEmailValidateException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1008,
|
||||
1117,
|
||||
];
|
||||
|
||||
/**
|
||||
* AtolEmailValidateException constructor.
|
||||
*
|
||||
* @param $email
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param \Throwable|null $previous
|
||||
*/
|
||||
public function __construct($email, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message ?: 'Invalid email: '.$email, $code, $previous);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при работе с АТОЛ Онлайн
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var int[] Теги ФФД
|
||||
*/
|
||||
protected $ffd_tags = null;
|
||||
|
||||
/**
|
||||
* AtolException constructor.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param \Throwable|null $previous
|
||||
*/
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
$message = $message ?: $this->message;
|
||||
if ($this->getFfdTags()) {
|
||||
$message .= ' [FFD tags: '.implode(', ', $this->getFfdTags()).']';
|
||||
}
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает теги ФФД, с которыми связано исключение
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getFfdTags(): ?array
|
||||
{
|
||||
return $this->ffd_tags;
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать ИНН некорректной длины
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolInnWrongLengthException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1016,
|
||||
1018,
|
||||
1226,
|
||||
1228,
|
||||
];
|
||||
|
||||
/**
|
||||
* AtolInnWrongLengthException constructor.
|
||||
*
|
||||
* @param $inn
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($inn, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message ?: 'INN length must be 10 or 12 digits only, but actual is '.
|
||||
valid_strlen($inn), $code, $previous);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при работе с невалидным JSON
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolInvalidJsonException extends AtolException
|
||||
{
|
||||
/**
|
||||
* AtolInnWrongLengthException constructor.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message ?: 'Invalid JSON: ['.json_last_error().'] '.json_last_error_msg(), $code, $previous);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при ошибке валидации UUID
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolInvalidUuidException extends AtolException
|
||||
{
|
||||
/**
|
||||
* AtolInvalidUuidException constructor.
|
||||
*
|
||||
* @param $uuid
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param \Throwable|null $previous
|
||||
*/
|
||||
public function __construct($uuid, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message ?: 'Invalid UUID: '.$uuid, $code, $previous);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать пустой логин ККТ
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolKktLoginEmptyException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'KKT login cannot be empty';
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать пустой пароль ККТ
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolKktPasswordEmptyException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'KKT password cannot be empty';
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинное имя
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolNameTooLongException extends AtolTooLongException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1026,
|
||||
1030,
|
||||
1085,
|
||||
1225,
|
||||
1227,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Name is too long';
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинный платёжный адрес
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolPaymentAddressTooLongException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1187,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Payment address is too long';
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинный телефон
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolPhoneTooLongException extends AtolTooLongException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1008,
|
||||
1073,
|
||||
1074,
|
||||
1075,
|
||||
1171,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Phone is too long';
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком высокую цену (сумму)
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolPriceTooHighException extends AtolTooManyException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1079,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Price is too high';
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинное что-либо
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolTooLongException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Parameter is too long';
|
||||
|
||||
/**
|
||||
* AtolTooLongException constructor.
|
||||
*
|
||||
* @param $string
|
||||
* @param $max
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($string, $max, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message ?: $this->message.' (max length - '.$max.', actual length - '.
|
||||
valid_strlen($string), $code, $previous);
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком большое количество чего-либо
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolTooManyException extends AtolException
|
||||
{
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Quantity is too high';
|
||||
|
||||
/**
|
||||
* AtolTooManyException constructor.
|
||||
*
|
||||
* @param $quantity
|
||||
* @param $max
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($quantity, $max, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
$message = $message ?: $this->message.' (max - '.$max.', actual - '.$quantity.')';
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке добавить слишком много платежей в массив
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolTooManyPaymentsException extends AtolTooManyException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1031,
|
||||
1081,
|
||||
1215,
|
||||
1217,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Too many payments';
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке добавить слишком много ставок НДС в массив
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolTooManyVatsException extends AtolTooManyException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1102,
|
||||
1103,
|
||||
1104,
|
||||
1105,
|
||||
1106,
|
||||
1107,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Too many vats';
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинный телефон
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolUnitTooLongException extends AtolTooLongException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1197,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'Measurement unit is too long';
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать слишком длинный дополнительный реквизит
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolUserdataTooLongException extends AtolTooLongException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $ffd_tags = [
|
||||
1191,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string Сообщение об ошибке
|
||||
*/
|
||||
protected $message = 'User data is too long';
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Исключение, возникающее при попытке указать некорректный тип документа
|
||||
*
|
||||
* @package AtolOnline\Exceptions
|
||||
*/
|
||||
class AtolWrongDocumentTypeException extends AtolException
|
||||
{
|
||||
/**
|
||||
* AtolWrongDocumentTypeException constructor.
|
||||
*
|
||||
* @param $type
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($type, $message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message ?: "Wrong document type: 'receipt' or 'correction' expected, but '$type' provided", $code, $previous);
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Traits;
|
||||
|
||||
use AtolOnline\{Constants\Constraints, Exceptions\AtolEmailTooLongException, Exceptions\AtolEmailValidateException};
|
||||
|
||||
/**
|
||||
* Добавляет объекту функционал для работы с email
|
||||
*
|
||||
* @package AtolOnline\Traits
|
||||
*/
|
||||
trait HasEmail
|
||||
{
|
||||
/**
|
||||
* @var string Почта
|
||||
*/
|
||||
protected $email;
|
||||
|
||||
/**
|
||||
* Возвращает установленную почту. Тег ФФД: 1008, 1117.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает почту. Тег ФФД: 1008, 1117.
|
||||
*
|
||||
* @param string $email
|
||||
* @return $this
|
||||
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException Слишком длинный email
|
||||
* @throws \AtolOnline\Exceptions\AtolEmailValidateException Невалидный email
|
||||
*/
|
||||
public function setEmail(string $email)
|
||||
{
|
||||
$email = trim($email);
|
||||
if (valid_strlen($email) > Constraints::MAX_LENGTH_EMAIL) {
|
||||
throw new AtolEmailTooLongException($email, Constraints::MAX_LENGTH_EMAIL);
|
||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new AtolEmailValidateException($email);
|
||||
}
|
||||
$this->email = $email;
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Traits;
|
||||
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Exceptions\AtolInnWrongLengthException;
|
||||
|
||||
/**
|
||||
* Добавляет объекту функционал для работы с ИНН
|
||||
*
|
||||
* @package AtolOnline\Traits
|
||||
*/
|
||||
trait HasInn
|
||||
{
|
||||
/**
|
||||
* @var string ИНН
|
||||
*/
|
||||
protected $inn;
|
||||
|
||||
/**
|
||||
* Возвращает установленный ИНН. Тег ФФД: 1228, 1018.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInn()
|
||||
{
|
||||
return $this->inn ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает ИНН. Тег ФФД: 1228, 1018.
|
||||
* Входная строка лишается всех знаков, кроме цифр.
|
||||
*
|
||||
* @param string $inn
|
||||
* @return $this
|
||||
* @throws AtolInnWrongLengthException Некорректная длина ИНН
|
||||
*/
|
||||
public function setInn(string $inn)
|
||||
{
|
||||
$inn = preg_replace("/[^0-9]/", '', $inn);
|
||||
if (preg_match_all(Constraints::PATTERN_INN, $inn) == 0) {
|
||||
throw new AtolInnWrongLengthException($inn);
|
||||
}
|
||||
$this->inn = $inn;
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
68
src/Collections/EntityCollection.php
Normal file
68
src/Collections/EntityCollection.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?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\Collections;
|
||||
|
||||
use AtolOnline\Exceptions\InvalidEntityInCollectionException;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Абстрактное описание коллекции любых сущностей
|
||||
*/
|
||||
abstract class EntityCollection extends Collection
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$this->checkCount();
|
||||
$this->checkItemsClasses();
|
||||
return parent::jsonSerialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет количество элементов коллекции
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkCount(): void
|
||||
{
|
||||
$this->isEmpty() && throw new (static::EMPTY_EXCEPTION_CLASS)();
|
||||
$this->count() > static::MAX_COUNT && throw new (static::TOO_MANY_EXCEPTION_CLASS)(static::MAX_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет корректность класса элемента коллекции
|
||||
*
|
||||
* @param mixed $item
|
||||
* @return void
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
public function checkItemClass(mixed $item): void
|
||||
{
|
||||
if (!is_object($item) || $item::class !== static::ENTITY_CLASS) {
|
||||
throw new InvalidEntityInCollectionException(static::class, static::ENTITY_CLASS, $item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет корректность классов элементов коллекции
|
||||
*
|
||||
* @return $this
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
public function checkItemsClasses(): self
|
||||
{
|
||||
return $this->each(fn($item) => $this->checkItemClass($item));
|
||||
}
|
||||
}
|
41
src/Collections/Items.php
Normal file
41
src/Collections/Items.php
Normal 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
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Collections;
|
||||
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Entities\Item;
|
||||
use AtolOnline\Exceptions\EmptyItemsException;
|
||||
use AtolOnline\Exceptions\TooManyItemsException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий коллекцию предметов расчёта для документа
|
||||
*/
|
||||
final class Items extends EntityCollection
|
||||
{
|
||||
/**
|
||||
* Класс объектов, находящихся в коллекции
|
||||
*/
|
||||
protected const ENTITY_CLASS = Item::class;
|
||||
|
||||
/**
|
||||
* Максмальное количество объектов в коллекции
|
||||
*/
|
||||
protected const MAX_COUNT = Constraints::MAX_COUNT_DOC_ITEMS;
|
||||
|
||||
/**
|
||||
* Класс исключения для выброса при пустой коллекции
|
||||
*/
|
||||
protected const EMPTY_EXCEPTION_CLASS = EmptyItemsException::class;
|
||||
|
||||
/**
|
||||
* Класс-наследник TooManyException для выброса при превышении количества
|
||||
*/
|
||||
protected const TOO_MANY_EXCEPTION_CLASS = TooManyItemsException::class;
|
||||
}
|
41
src/Collections/Payments.php
Normal file
41
src/Collections/Payments.php
Normal 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
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Collections;
|
||||
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Entities\Payment;
|
||||
use AtolOnline\Exceptions\EmptyPaymentsException;
|
||||
use AtolOnline\Exceptions\TooManyPaymentsException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий коллекцию оплат для документа
|
||||
*/
|
||||
final class Payments extends EntityCollection
|
||||
{
|
||||
/**
|
||||
* Класс объектов, находящихся в коллекции
|
||||
*/
|
||||
protected const ENTITY_CLASS = Payment::class;
|
||||
|
||||
/**
|
||||
* Максмальное количество объектов в коллекции
|
||||
*/
|
||||
protected const MAX_COUNT = Constraints::MAX_COUNT_DOC_PAYMENTS;
|
||||
|
||||
/**
|
||||
* Класс исключения для выброса при пустой коллекции
|
||||
*/
|
||||
protected const EMPTY_EXCEPTION_CLASS = EmptyPaymentsException::class;
|
||||
|
||||
/**
|
||||
* Класс-наследник TooManyException для выброса при превышении количества
|
||||
*/
|
||||
protected const TOO_MANY_EXCEPTION_CLASS = TooManyPaymentsException::class;
|
||||
}
|
41
src/Collections/Vats.php
Normal file
41
src/Collections/Vats.php
Normal 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
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Collections;
|
||||
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Entities\Vat;
|
||||
use AtolOnline\Exceptions\EmptyVatsException;
|
||||
use AtolOnline\Exceptions\TooManyVatsException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий коллекцию ставок НДС для документа
|
||||
*/
|
||||
final class Vats extends EntityCollection
|
||||
{
|
||||
/**
|
||||
* Класс объектов, находящихся в коллекции
|
||||
*/
|
||||
protected const ENTITY_CLASS = Vat::class;
|
||||
|
||||
/**
|
||||
* Максмальное количество объектов в коллекции
|
||||
*/
|
||||
protected const MAX_COUNT = Constraints::MAX_COUNT_DOC_VATS;
|
||||
|
||||
/**
|
||||
* Класс исключения для выброса при пустой коллекции
|
||||
*/
|
||||
protected const EMPTY_EXCEPTION_CLASS = EmptyVatsException::class;
|
||||
|
||||
/**
|
||||
* Класс-наследник TooManyException для выброса при превышении количества
|
||||
*/
|
||||
protected const TOO_MANY_EXCEPTION_CLASS = TooManyVatsException::class;
|
||||
}
|
220
src/Constants/Constraints.php
Normal file
220
src/Constants/Constraints.php
Normal file
@ -0,0 +1,220 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Constants;
|
||||
|
||||
/**
|
||||
* Класс с константами ограничений
|
||||
*/
|
||||
final class Constraints
|
||||
{
|
||||
/**
|
||||
* Максимальная длина Callback URL
|
||||
*/
|
||||
const MAX_LENGTH_CALLBACK_URL = 256;
|
||||
|
||||
/**
|
||||
* Максимальная длина email
|
||||
*/
|
||||
const MAX_LENGTH_EMAIL = 64;
|
||||
|
||||
/**
|
||||
* Максимальная длина логина ККТ
|
||||
*/
|
||||
const MAX_LENGTH_LOGIN = 100;
|
||||
|
||||
/**
|
||||
* Максимальная длина пароля ККТ
|
||||
*/
|
||||
const MAX_LENGTH_PASSWORD = 100;
|
||||
|
||||
/**
|
||||
* Максимальная длина адреса места расчётов
|
||||
*/
|
||||
const MAX_LENGTH_PAYMENT_ADDRESS = 256;
|
||||
|
||||
/**
|
||||
* Максимальная длина наименования покупателя (1227)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
|
||||
*/
|
||||
const MAX_LENGTH_CLIENT_NAME = 256;
|
||||
|
||||
/**
|
||||
* Максимальная длина наименования предмета расчёта (1030)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_LENGTH_ITEM_NAME = 128;
|
||||
|
||||
/**
|
||||
* Максимальная цена за единицу предмета расчёта (1079)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_COUNT_ITEM_PRICE = 42949672.95;
|
||||
|
||||
/**
|
||||
* Максимальное количество (вес) единицы предмета расчёта (1023)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_COUNT_ITEM_QUANTITY = 99999.999;
|
||||
|
||||
/**
|
||||
* Максимальная стоимость всех предметов расчёта в документе прихода, расхода,
|
||||
* возврата прихода, возврата расхода (1043)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_COUNT_ITEM_SUM = 42949672.95;
|
||||
|
||||
/**
|
||||
* Максимальная длина телефона или email покупателя (1008)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
|
||||
*/
|
||||
const MAX_LENGTH_CLIENT_CONTACT = 64;
|
||||
|
||||
/**
|
||||
* Длина операции для платёжного агента (1044)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19
|
||||
*/
|
||||
const MAX_LENGTH_PAYING_AGENT_OPERATION = 24;
|
||||
|
||||
/**
|
||||
* Максимальное количество предметов расчёта в документе прихода, расхода, возврата прихода, возврата расхода
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_COUNT_DOC_ITEMS = 100;
|
||||
|
||||
/**
|
||||
* Максимальная длина единицы измерения предмета расчётов
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_LENGTH_MEASUREMENT_UNIT = 16;
|
||||
|
||||
/**
|
||||
* Максимальная длина пользовательских данных для предмета расчётов (1191)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29
|
||||
*/
|
||||
const MAX_LENGTH_USER_DATA = 64;
|
||||
|
||||
/**
|
||||
* Минимальная длина кода таможенной декларации (1231)
|
||||
*
|
||||
* @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/items/declaration_number"
|
||||
*/
|
||||
const MIN_LENGTH_DECLARATION_NUMBER = 1;
|
||||
|
||||
/**
|
||||
* Максимальная длина кода таможенной декларации (1231)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30
|
||||
*/
|
||||
const MAX_LENGTH_DECLARATION_NUMBER = 32;
|
||||
|
||||
/**
|
||||
* Максимальное количество платежей в любом документе
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30 и 35
|
||||
*/
|
||||
const MAX_COUNT_DOC_PAYMENTS = 10;
|
||||
|
||||
/**
|
||||
* Максимальное количество ставок НДС в любом документе
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 31 и 36
|
||||
*/
|
||||
const MAX_COUNT_DOC_VATS = 6;
|
||||
|
||||
/**
|
||||
* Максимальная сумма одной оплаты
|
||||
*/
|
||||
const MAX_COUNT_PAYMENT_SUM = 99999.999;
|
||||
|
||||
/**
|
||||
* Максимальная длина имени кассира (1021)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32
|
||||
*/
|
||||
const MAX_LENGTH_CASHIER_NAME = 64;
|
||||
|
||||
/**
|
||||
* Максимальная длина кода товара в байтах (1162)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21
|
||||
*/
|
||||
const MAX_LENGTH_ITEM_CODE = 32;
|
||||
|
||||
/**
|
||||
* Максимальная длина значения дополнительного реквизита чека (1192)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32
|
||||
*/
|
||||
const MAX_LENGTH_ADD_CHECK_PROP = 16;
|
||||
|
||||
/**
|
||||
* Максимальная длина наименования дополнительного реквизита пользователя (1085)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32
|
||||
*/
|
||||
const MAX_LENGTH_ADD_USER_PROP_NAME = 64;
|
||||
|
||||
/**
|
||||
* Максимальная длина значения дополнительного реквизита пользователя (1086)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32
|
||||
*/
|
||||
const MAX_LENGTH_ADD_USER_PROP_VALUE = 256;
|
||||
|
||||
/**
|
||||
* Формат даты документа коррекции
|
||||
*/
|
||||
const CORRECTION_DATE_FORMAT = 'd.m.Y';
|
||||
|
||||
/**
|
||||
* Регулярное выражение для валидации строки ИНН
|
||||
*
|
||||
* @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/client/inn"
|
||||
*/
|
||||
const PATTERN_INN
|
||||
= /* @lang PhpRegExp */
|
||||
'/(^[\d]{10}$)|(^[\d]{12}$)/';
|
||||
|
||||
/**
|
||||
* Регулярное выражение для валидации номера телефона
|
||||
*
|
||||
* @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/definitions/phone_number"
|
||||
*/
|
||||
const PATTERN_PHONE
|
||||
= /* @lang PhpRegExp */
|
||||
'/^([^\s\\\]{0,17}|\+[^\s\\\]{1,18})$/';
|
||||
|
||||
/**
|
||||
* Регулярное выражение для валидации строки Callback URL
|
||||
*/
|
||||
const PATTERN_CALLBACK_URL
|
||||
= /* @lang PhpRegExp */
|
||||
'/^http(s?):\/\/[0-9a-zA-Zа-яА-Я]' .
|
||||
'([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-.?,\'\/\\\+&=%\$#_]*)?$/';
|
||||
|
||||
/**
|
||||
* Регулярное выражение для валидации кода страны происхождения товара
|
||||
*/
|
||||
const PATTERN_OKSM_CODE
|
||||
= /* @lang PhpRegExp */
|
||||
'/^[\d]{3}$/';
|
||||
}
|
249
src/Constants/Ffd105Tags.php
Normal file
249
src/Constants/Ffd105Tags.php
Normal file
@ -0,0 +1,249 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Constants;
|
||||
|
||||
/**
|
||||
* Константы тегов ФФД 1.05
|
||||
*/
|
||||
final class Ffd105Tags
|
||||
{
|
||||
/**
|
||||
* Телефон или электронный адрес покупателя
|
||||
*/
|
||||
const CLIENT_PHONE_EMAIL = 1008;
|
||||
|
||||
/**
|
||||
* Наименование организации или фамилия, имя, отчество (при наличии), серия и номер паспорта покупателя (клиента)
|
||||
*/
|
||||
const CLIENT_NAME = 1227;
|
||||
|
||||
/**
|
||||
* ИНН организации или покупателя (клиента)
|
||||
*/
|
||||
const CLIENT_INN = 1228;
|
||||
|
||||
/**
|
||||
* Адрес электронной почты отправителя чека
|
||||
*/
|
||||
const COMPANY_EMAIL = 1117;
|
||||
|
||||
/**
|
||||
* ИНН пользователя
|
||||
*/
|
||||
const COMPANY_INN = 1008;
|
||||
|
||||
/**
|
||||
* Применяемая система налогообложения
|
||||
*/
|
||||
const COMPANY_SNO = 1055;
|
||||
|
||||
/**
|
||||
* Место расчётов
|
||||
*/
|
||||
const COMPANY_PADDRESS = 1187;
|
||||
|
||||
/**
|
||||
* Признак агента по предмету расчёта
|
||||
*/
|
||||
const AGENT_TYPE = 1222;
|
||||
|
||||
/**
|
||||
* Телефон оператора по приёму платежей
|
||||
*/
|
||||
const RPO_PHONES = 1074;
|
||||
|
||||
/**
|
||||
* Телефон оператора перевода
|
||||
*/
|
||||
const MTO_PHONES = 1075;
|
||||
|
||||
/**
|
||||
* ИНН оператора перевода
|
||||
*/
|
||||
const MTO_INN = 1016;
|
||||
|
||||
/**
|
||||
* Телефон платёжного агента
|
||||
*/
|
||||
const PAGENT_PHONE = 1073;
|
||||
|
||||
/**
|
||||
* Телефон поставщика
|
||||
*/
|
||||
const SUPPLIER_PHONES = 1171;
|
||||
|
||||
/**
|
||||
* Наименование поставщика
|
||||
*/
|
||||
const SUPPLIER_NAME = 1225;
|
||||
|
||||
/**
|
||||
* ИНН поставщика
|
||||
*/
|
||||
const SUPPLIER_INN = 1226;
|
||||
|
||||
/**
|
||||
* Кассир
|
||||
*/
|
||||
const CASHIER = 1021;
|
||||
|
||||
/**
|
||||
* Наименование предмета расчёта
|
||||
*/
|
||||
const ITEM_NAME = 1030;
|
||||
|
||||
/**
|
||||
* Цена за единицу предмета расчёта с учётом скидок и наценок
|
||||
*/
|
||||
const ITEM_PRICE = 1079;
|
||||
|
||||
/**
|
||||
* Количество предмета расчёта
|
||||
*/
|
||||
const ITEM_QUANTITY = 1023;
|
||||
|
||||
/**
|
||||
* Стоимость предмета расчёта с учётом скидок и наценок
|
||||
*/
|
||||
const ITEM_SUM = 1043;
|
||||
|
||||
/**
|
||||
* Единица измерения предмета расчёта
|
||||
*/
|
||||
const ITEM_MEASUREMENT_UNIT = 1197;
|
||||
|
||||
/**
|
||||
* Код товара
|
||||
*/
|
||||
const ITEM_NOMENCLATURE_CODE = 1162;
|
||||
|
||||
/**
|
||||
* Признак способа расчёта
|
||||
*/
|
||||
const ITEM_PAYMENT_METHOD = 1214;
|
||||
|
||||
/**
|
||||
* Признак предмета расчёта
|
||||
*/
|
||||
const ITEM_PAYMENT_OBJECT = 1212;
|
||||
|
||||
/**
|
||||
* Дополнительный реквизит предмета расчёта
|
||||
*/
|
||||
const ITEM_USERDATA = 1191;
|
||||
|
||||
/**
|
||||
* Сумма акциза с учётом копеек, включённая в стоимость предмета расчёта
|
||||
*/
|
||||
const ITEM_EXCISE = 1229;
|
||||
|
||||
/**
|
||||
* Цифровой код страны происхождения товара в соответствии с Общероссийским классификатором стран мира
|
||||
*
|
||||
* @see https://ru.wikipedia.org/wiki/Общероссийский_классификатор_стран_мира
|
||||
* @see https://classifikators.ru/oksm
|
||||
*/
|
||||
const ITEM_COUNTRY_CODE = 1230;
|
||||
|
||||
/**
|
||||
* Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155)
|
||||
*/
|
||||
const ITEM_DECLARATION_NUMBER = 1231;
|
||||
|
||||
/**
|
||||
* Тип коррекции
|
||||
*/
|
||||
const CORRECTION_TYPE = 1173;
|
||||
|
||||
/**
|
||||
* Дата документа основания для коррекции
|
||||
*/
|
||||
const CORRECTION_DATE = 1178;
|
||||
|
||||
/**
|
||||
* Сумма по чеку (БСО) наличными
|
||||
*/
|
||||
const PAYMENT_TYPE_CASH = 1031;
|
||||
|
||||
/**
|
||||
* Сумма по чеку безналичными
|
||||
*/
|
||||
const PAYMENT_TYPE_ELECTRON = 1081;
|
||||
|
||||
/**
|
||||
* Сумма по чеку предоплатой
|
||||
*/
|
||||
const PAYMENT_TYPE_PREPAID = 1215;
|
||||
|
||||
/**
|
||||
* Сумма по чеку постоплатой
|
||||
*/
|
||||
const PAYMENT_TYPE_CREDIT = 1216;
|
||||
|
||||
/**
|
||||
* Сумма по чеку встречным представлением
|
||||
*/
|
||||
const PAYMENT_TYPE_OTHER = 1217;
|
||||
|
||||
/**
|
||||
* Ставка НДС
|
||||
*/
|
||||
const ITEM_VAT_TYPE = 1199;
|
||||
|
||||
/**
|
||||
* Сумма расчета по чеку без НДС
|
||||
*/
|
||||
const DOC_VAT_TYPE_NONE = 1105;
|
||||
|
||||
/**
|
||||
* Сумма расчета по чеку с НДС по ставке 0%
|
||||
*/
|
||||
const DOC_VAT_TYPE_VAT0 = 1104;
|
||||
|
||||
/**
|
||||
* Сумма НДС чека по ставке 10%
|
||||
*/
|
||||
const DOC_VAT_TYPE_VAT10 = 1103;
|
||||
|
||||
/**
|
||||
* Сумма НДС чека по ставке 20%
|
||||
*/
|
||||
const DOC_VAT_TYPE_VAT20 = 1102;
|
||||
|
||||
/**
|
||||
* Сумма НДС чека по расч. ставке 10/110
|
||||
*/
|
||||
const DOC_VAT_TYPE_VAT110 = 1107;
|
||||
|
||||
/**
|
||||
* Сумма НДС чека по расч. ставке 20/120
|
||||
*/
|
||||
const DOC_VAT_TYPE_VAT120 = 1106;
|
||||
|
||||
/**
|
||||
* Значение дополнительного реквизита чека
|
||||
*/
|
||||
const DOC_ADD_CHECK_PROP_VALUE = 1192;
|
||||
|
||||
/**
|
||||
* Дополнительный реквизит пользователя
|
||||
*/
|
||||
const DOC_ADD_USER_PROP = 1084;
|
||||
|
||||
/**
|
||||
* Наименование дополнительного реквизита пользователя
|
||||
*/
|
||||
const DOC_ADD_USER_PROP_NAME = 1085;
|
||||
|
||||
/**
|
||||
* Значение дополнительного реквизита пользователя
|
||||
*/
|
||||
const DOC_ADD_USER_PROP_VALUE = 1086;
|
||||
}
|
132
src/Entities/AdditionalUserProps.php
Normal file
132
src/Entities/AdditionalUserProps.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?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\{
|
||||
EmptyAddUserPropNameException,
|
||||
EmptyAddUserPropValueException,
|
||||
TooLongAddUserPropNameException,
|
||||
TooLongAddUserPropValueException
|
||||
};
|
||||
use JetBrains\PhpStorm\{
|
||||
ArrayShape,
|
||||
Pure
|
||||
};
|
||||
|
||||
/**
|
||||
* Класс, описывающий дополнительный реквизит пользователя
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32
|
||||
*/
|
||||
final class AdditionalUserProps extends Entity
|
||||
{
|
||||
/**
|
||||
* @var string Наименование (1085)
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* @var string Значение (1086)
|
||||
*/
|
||||
protected string $value;
|
||||
|
||||
/**
|
||||
* Конструктор объекта покупателя
|
||||
*
|
||||
* @param string $name Наименование (1227)
|
||||
* @param string $value Значение (1008)
|
||||
* @throws EmptyAddUserPropNameException
|
||||
* @throws EmptyAddUserPropValueException
|
||||
* @throws TooLongAddUserPropNameException
|
||||
* @throws TooLongAddUserPropValueException
|
||||
*/
|
||||
public function __construct(string $name, string $value)
|
||||
{
|
||||
$this->setName($name)->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает наименование реквизита
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает наименование реквизита
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
* @throws TooLongAddUserPropNameException
|
||||
* @throws EmptyAddUserPropNameException
|
||||
*/
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$name = trim($name);
|
||||
if (mb_strlen($name) > Constraints::MAX_LENGTH_ADD_USER_PROP_NAME) {
|
||||
throw new TooLongAddUserPropNameException($name);
|
||||
}
|
||||
if (empty($name)) {
|
||||
throw new EmptyAddUserPropNameException($name);
|
||||
}
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный телефон
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getValue(): ?string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает значение реквизита
|
||||
*
|
||||
* @param string $value
|
||||
* @return $this
|
||||
* @throws TooLongAddUserPropValueException
|
||||
* @throws EmptyAddUserPropValueException
|
||||
*/
|
||||
public function setValue(string $value): self
|
||||
{
|
||||
$value = trim($value);
|
||||
if (mb_strlen($value) > Constraints::MAX_LENGTH_CLIENT_NAME) {
|
||||
throw new TooLongAddUserPropValueException($value);
|
||||
}
|
||||
if (empty($value)) {
|
||||
throw new EmptyAddUserPropValueException($value);
|
||||
}
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
#[ArrayShape(['name' => 'string', 'value' => 'null|string'])]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->getName(),
|
||||
'value' => $this->getValue(),
|
||||
];
|
||||
}
|
||||
}
|
169
src/Entities/AgentInfo.php
Normal file
169
src/Entities/AgentInfo.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Enums\AgentTypes;
|
||||
use AtolOnline\Exceptions\InvalidEnumValueException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий данные агента
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 26-28
|
||||
*/
|
||||
final class AgentInfo extends Entity
|
||||
{
|
||||
/**
|
||||
* @var string|null Признак агента (1057)
|
||||
*/
|
||||
protected ?string $type = null;
|
||||
|
||||
/**
|
||||
* @var PayingAgent|null Платёжный агент
|
||||
*/
|
||||
protected ?PayingAgent $paying_agent = null;
|
||||
|
||||
/**
|
||||
* @var ReceivePaymentsOperator|null Оператор по приёму платежей
|
||||
*/
|
||||
protected ?ReceivePaymentsOperator $receive_payments_operator = null;
|
||||
|
||||
/**
|
||||
* @var MoneyTransferOperator|null Оператор перевода
|
||||
*/
|
||||
protected ?MoneyTransferOperator $money_transfer_operator = null;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string|null $type Признак агента (1057)
|
||||
* @param PayingAgent|null $pagent Платёжный агент
|
||||
* @param ReceivePaymentsOperator|null $rp_operator Оператор по приёму платежей
|
||||
* @param MoneyTransferOperator|null $mt_operator Оператор перевода
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function __construct(
|
||||
?string $type = null,
|
||||
?PayingAgent $pagent = null,
|
||||
?ReceivePaymentsOperator $rp_operator = null,
|
||||
?MoneyTransferOperator $mt_operator = null,
|
||||
) {
|
||||
!is_null($type) && $this->setType($type);
|
||||
!is_null($pagent) && $this->setPayingAgent($pagent);
|
||||
!is_null($rp_operator) && $this->setReceivePaymentsOperator($rp_operator);
|
||||
!is_null($mt_operator) && $this->setMoneyTransferOperator($mt_operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный признак оператора
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает признак оператора
|
||||
*
|
||||
* @param string|null $type
|
||||
* @return AgentInfo
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setType(?string $type): self
|
||||
{
|
||||
AgentTypes::isValid($type) && $this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Взвращает установленного платёжного агента
|
||||
*
|
||||
* @return PayingAgent|null
|
||||
*/
|
||||
public function getPayingAgent(): ?PayingAgent
|
||||
{
|
||||
return $this->paying_agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает платёжного агента
|
||||
*
|
||||
* @param PayingAgent|null $agent
|
||||
* @return AgentInfo
|
||||
*/
|
||||
public function setPayingAgent(?PayingAgent $agent): self
|
||||
{
|
||||
$this->paying_agent = $agent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного оператора по приёму платежей
|
||||
*
|
||||
* @return ReceivePaymentsOperator|null
|
||||
*/
|
||||
public function getReceivePaymentsOperator(): ?ReceivePaymentsOperator
|
||||
{
|
||||
return $this->receive_payments_operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает оператора по приёму платежей
|
||||
*
|
||||
* @param ReceivePaymentsOperator|null $operator
|
||||
* @return AgentInfo
|
||||
*/
|
||||
public function setReceivePaymentsOperator(?ReceivePaymentsOperator $operator): self
|
||||
{
|
||||
$this->receive_payments_operator = $operator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного оператора перевода
|
||||
*
|
||||
* @return MoneyTransferOperator|null
|
||||
*/
|
||||
public function getMoneyTransferOperator(): ?MoneyTransferOperator
|
||||
{
|
||||
return $this->money_transfer_operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает оператора перевода
|
||||
*
|
||||
* @param MoneyTransferOperator|null $operator
|
||||
* @return AgentInfo
|
||||
*/
|
||||
public function setMoneyTransferOperator(?MoneyTransferOperator $operator): self
|
||||
{
|
||||
$this->money_transfer_operator = $operator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [];
|
||||
$this->getType() && $json['type'] = $this->getType();
|
||||
$this->getPayingAgent()?->jsonSerialize() && $json['paying_agent'] = $this
|
||||
->getPayingAgent()->jsonSerialize();
|
||||
$this->getReceivePaymentsOperator()?->jsonSerialize() && $json['receive_payments_operator'] = $this
|
||||
->getReceivePaymentsOperator()->jsonSerialize();
|
||||
$this->getMoneyTransferOperator()?->jsonSerialize() && $json['money_transfer_operator'] = $this
|
||||
->getMoneyTransferOperator()->jsonSerialize();
|
||||
return $json;
|
||||
}
|
||||
}
|
141
src/Entities/Client.php
Normal file
141
src/Entities/Client.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?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\{
|
||||
InvalidEmailException,
|
||||
InvalidInnLengthException,
|
||||
InvalidPhoneException,
|
||||
TooLongClientNameException,
|
||||
TooLongEmailException};
|
||||
use AtolOnline\Traits\{
|
||||
HasEmail,
|
||||
HasInn};
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
/**
|
||||
* Класс, описывающий покупателя
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
|
||||
*/
|
||||
final class Client extends Entity
|
||||
{
|
||||
use HasEmail, HasInn;
|
||||
|
||||
/**
|
||||
* @var string|null Наименование (1227)
|
||||
*/
|
||||
protected ?string $name = null;
|
||||
|
||||
/**
|
||||
* @var string|null Телефон (1008)
|
||||
*/
|
||||
protected ?string $phone = null;
|
||||
|
||||
/**
|
||||
* Конструктор объекта покупателя
|
||||
*
|
||||
* @param string|null $name Наименование (1227)
|
||||
* @param string|null $email Телефон (1008)
|
||||
* @param string|null $phone Email (1008)
|
||||
* @param string|null $inn ИНН (1228)
|
||||
* @throws InvalidEmailException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPhoneException
|
||||
* @throws TooLongClientNameException
|
||||
* @throws TooLongEmailException
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $email = null,
|
||||
?string $phone = null,
|
||||
?string $inn = null
|
||||
) {
|
||||
!is_null($name) && $this->setName($name);
|
||||
!is_null($email) && $this->setEmail($email);
|
||||
!is_null($phone) && $this->setPhone($phone);
|
||||
!is_null($inn) && $this->setInn($inn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает наименование покупателя
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает наименование покупателя
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
* @throws TooLongClientNameException
|
||||
*/
|
||||
public function setName(?string $name): self
|
||||
{
|
||||
if (is_string($name)) {
|
||||
$name = preg_replace('/[\n\r\t]/', '', trim($name));
|
||||
if (mb_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) {
|
||||
throw new TooLongClientNameException($name);
|
||||
}
|
||||
}
|
||||
$this->name = $name ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный телефон
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPhone(): ?string
|
||||
{
|
||||
return $this->phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает телефон
|
||||
*
|
||||
* @param string|null $phone Номер телефона
|
||||
* @return $this
|
||||
* @throws InvalidPhoneException
|
||||
*/
|
||||
public function setPhone(?string $phone): self
|
||||
{
|
||||
if (is_string($phone)) {
|
||||
$phone = preg_replace('/[^\d]/', '', trim($phone));
|
||||
if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) {
|
||||
throw new InvalidPhoneException($phone);
|
||||
}
|
||||
}
|
||||
$this->phone = empty($phone) ? null : "+$phone";
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [];
|
||||
!is_null($this->getName()) && $json['name'] = $this->getName();
|
||||
!is_null($this->getEmail()) && $json['email'] = $this->getEmail();
|
||||
!is_null($this->getPhone()) && $json['phone'] = $this->getPhone();
|
||||
!is_null($this->getInn()) && $json['inn'] = $this->getInn();
|
||||
return $json;
|
||||
}
|
||||
}
|
156
src/Entities/Company.php
Normal file
156
src/Entities/Company.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?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,
|
||||
Traits\HasEmail,
|
||||
Traits\HasInn
|
||||
};
|
||||
use AtolOnline\Exceptions\{
|
||||
InvalidEmailException,
|
||||
InvalidEnumValueException,
|
||||
InvalidInnLengthException,
|
||||
InvalidPaymentAddressException,
|
||||
TooLongEmailException,
|
||||
TooLongPaymentAddressException
|
||||
};
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
|
||||
/**
|
||||
* Класс, описывающий сущность компании-продавца
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
|
||||
*/
|
||||
final class Company extends Entity
|
||||
{
|
||||
use HasEmail, HasInn;
|
||||
|
||||
/**
|
||||
* @var string|null Система налогообложения продавца (1055)
|
||||
*/
|
||||
protected ?string $sno;
|
||||
|
||||
/**
|
||||
* @var string|null Место расчётов (адрес интернет-магазина) (1187)
|
||||
*/
|
||||
protected ?string $payment_address;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $sno Система налогообложения продавца (1055)
|
||||
* @param string $inn ИНН (1018)
|
||||
* @param string $payment_address Место расчётов (адрес интернет-магазина) (1187)
|
||||
* @param string $email Почта (1117)
|
||||
* @throws InvalidEmailException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws InvalidEnumValueException
|
||||
* @throws TooLongEmailException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function __construct(
|
||||
string $email, //TODO сделать необязательным здесь
|
||||
string $sno, //TODO сделать необязательным здесь
|
||||
string $inn,
|
||||
string $payment_address,
|
||||
) {
|
||||
$this->setEmail($email)->setSno($sno)->setInn($inn)->setPaymentAddress($payment_address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный тип налогообложения
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSno(): string
|
||||
{
|
||||
return $this->sno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип налогообложения
|
||||
*
|
||||
* @param string $sno
|
||||
* @return $this
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setSno(string $sno): self
|
||||
{
|
||||
$sno = trim($sno);
|
||||
SnoTypes::isValid($sno) && $this->sno = $sno;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный адрес места расчётов
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentAddress(): string
|
||||
{
|
||||
return $this->payment_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает адрес места расчётов
|
||||
*
|
||||
* @param string $payment_address
|
||||
* @return $this
|
||||
* @throws TooLongPaymentAddressException
|
||||
* @throws InvalidPaymentAddressException
|
||||
*/
|
||||
public function setPaymentAddress(string $payment_address): self
|
||||
{
|
||||
$payment_address = trim($payment_address);
|
||||
if (empty($payment_address)) {
|
||||
throw new InvalidPaymentAddressException();
|
||||
} elseif (mb_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) {
|
||||
throw new TooLongPaymentAddressException($payment_address);
|
||||
}
|
||||
$this->payment_address = $payment_address;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws InvalidEmailException
|
||||
* @throws InvalidEnumValueException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'email' => 'string',
|
||||
'sno' => 'string',
|
||||
'inn' => 'string',
|
||||
'payment_address' => 'string',
|
||||
])]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'email' => $this->email
|
||||
? $this->getEmail()
|
||||
: throw new InvalidEmailException(),
|
||||
'sno' => $this->sno
|
||||
? $this->getSno()
|
||||
: throw new InvalidEnumValueException(SnoTypes::class, 'null'),
|
||||
'inn' => $this->inn
|
||||
? $this->getInn()
|
||||
: throw new InvalidInnLengthException(),
|
||||
'payment_address' => $this->payment_address
|
||||
? $this->getPaymentAddress()
|
||||
: throw new InvalidPaymentAddressException(),
|
||||
];
|
||||
}
|
||||
}
|
273
src/Entities/Correction.php
Normal file
273
src/Entities/Correction.php
Normal file
@ -0,0 +1,273 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\{
|
||||
Api\AtolResponse,
|
||||
Api\Fiscalizer,
|
||||
Collections\Payments,
|
||||
Collections\Vats,
|
||||
Constants\Constraints};
|
||||
use AtolOnline\Exceptions\{
|
||||
AuthFailedException,
|
||||
EmptyLoginException,
|
||||
EmptyPasswordException,
|
||||
InvalidEntityInCollectionException,
|
||||
InvalidInnLengthException,
|
||||
InvalidPaymentAddressException,
|
||||
TooLongCashierException,
|
||||
TooLongPaymentAddressException};
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
|
||||
/**
|
||||
* Класс, описывающий документ коррекции
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
|
||||
*/
|
||||
final class Correction extends Entity
|
||||
{
|
||||
/**
|
||||
* Тип документа
|
||||
*/
|
||||
public const DOC_TYPE = 'correction';
|
||||
|
||||
/**
|
||||
* @var Company Продавец
|
||||
*/
|
||||
protected Company $company;
|
||||
|
||||
/**
|
||||
* @todo вынести в трейт?
|
||||
* @var string|null ФИО кассира
|
||||
*/
|
||||
protected ?string $cashier = null;
|
||||
|
||||
/**
|
||||
* @var CorrectionInfo Данные коррекции
|
||||
*/
|
||||
protected CorrectionInfo $correction_info;
|
||||
|
||||
/**
|
||||
* @var Payments Коллекция оплат
|
||||
*/
|
||||
protected Payments $payments;
|
||||
|
||||
/**
|
||||
* @var Vats Коллекция ставок НДС
|
||||
*/
|
||||
protected Vats $vats;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param Company $company
|
||||
* @param CorrectionInfo $correction_info
|
||||
* @param Payments $payments
|
||||
* @param Vats $vats
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(
|
||||
Company $company,
|
||||
CorrectionInfo $correction_info,
|
||||
Payments $payments,
|
||||
Vats $vats,
|
||||
) {
|
||||
$this->setCompany($company)->setCorrectionInfo($correction_info)->setPayments($payments)->setVats($vats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного продавца
|
||||
*
|
||||
* @return Company
|
||||
*/
|
||||
public function getCompany(): Company
|
||||
{
|
||||
return $this->company;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает продавца
|
||||
*
|
||||
* @param Company $company
|
||||
* @return $this
|
||||
*/
|
||||
public function setCompany(Company $company): self
|
||||
{
|
||||
$this->company = $company;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного кассира
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCashier(): ?string
|
||||
{
|
||||
return $this->cashier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает кассира
|
||||
*
|
||||
* @param string|null $cashier
|
||||
* @return $this
|
||||
* @throws TooLongCashierException
|
||||
*/
|
||||
public function setCashier(?string $cashier): self
|
||||
{
|
||||
if (is_string($cashier)) {
|
||||
$cashier = trim($cashier);
|
||||
if (mb_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
|
||||
throw new TooLongCashierException($cashier);
|
||||
}
|
||||
}
|
||||
$this->cashier = $cashier ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленные данные коррекции
|
||||
*
|
||||
* @return CorrectionInfo
|
||||
*/
|
||||
public function getCorrectionInfo(): CorrectionInfo
|
||||
{
|
||||
return $this->correction_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает данные коррекции
|
||||
*
|
||||
* @param CorrectionInfo $correction_info
|
||||
* @return Correction
|
||||
*/
|
||||
public function setCorrectionInfo(CorrectionInfo $correction_info): Correction
|
||||
{
|
||||
$this->correction_info = $correction_info;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную коллекцию оплат
|
||||
*
|
||||
* @return Payments
|
||||
*/
|
||||
public function getPayments(): Payments
|
||||
{
|
||||
return $this->payments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает коллекцию оплат
|
||||
*
|
||||
* @param Payments $payments
|
||||
* @return $this
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
public function setPayments(Payments $payments): self
|
||||
{
|
||||
$payments->checkCount();
|
||||
$payments->checkItemsClasses();
|
||||
$this->payments = $payments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную коллекцию ставок НДС
|
||||
*
|
||||
* @return Vats|null
|
||||
*/
|
||||
public function getVats(): Vats
|
||||
{
|
||||
return $this->vats ?? new Vats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает коллекцию ставок НДС
|
||||
*
|
||||
* @param Vats|null $vats
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setVats(Vats $vats): self
|
||||
{
|
||||
$vats->checkCount();
|
||||
$vats->checkItemsClasses();
|
||||
$this->vats = $vats;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует коррекцию прихода по текущему документу
|
||||
*
|
||||
* @param Fiscalizer $fiscalizer Объект фискализатора
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function sellCorrect(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $fiscalizer->sellCorrect($this, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует коррекцию расхода по текущему документу
|
||||
*
|
||||
* @param Fiscalizer $fiscalizer Объект фискализатора
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function buyCorrect(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $fiscalizer->buyCorrect($this, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'company' => '\AtolOnline\Entities\Company',
|
||||
'correction_info' => '\AtolOnline\Entities\CorrectionInfo',
|
||||
'payments' => 'array',
|
||||
'vats' => '\AtolOnline\Collections\Vats|null',
|
||||
'cashier' => 'null|string',
|
||||
])]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [
|
||||
'company' => $this->getCompany(),
|
||||
'correction_info' => $this->getCorrectionInfo(),
|
||||
'payments' => $this->getPayments()->jsonSerialize(),
|
||||
'vats' => $this->getVats(),
|
||||
];
|
||||
!is_null($this->getCashier()) && $json['cashier'] = $this->getCashier();
|
||||
return $json;
|
||||
}
|
||||
}
|
155
src/Entities/CorrectionInfo.php
Normal file
155
src/Entities/CorrectionInfo.php
Normal file
@ -0,0 +1,155 @@
|
||||
<?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\Enums\CorrectionTypes;
|
||||
use AtolOnline\Exceptions\{
|
||||
EmptyCorrectionNumberException,
|
||||
InvalidCorrectionDateException,
|
||||
InvalidEnumValueException};
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\{
|
||||
ArrayShape,
|
||||
Pure};
|
||||
|
||||
/**
|
||||
* Класс, описывающий данные коррекции
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
|
||||
*/
|
||||
final class CorrectionInfo extends Entity
|
||||
{
|
||||
/**
|
||||
* @var string|null Тип коррекции (1173)
|
||||
*/
|
||||
protected ?string $type = null;
|
||||
|
||||
/**
|
||||
* @var string|null Дата документа основания для коррекции (1178)
|
||||
*/
|
||||
protected ?string $date = null;
|
||||
|
||||
/**
|
||||
* @var string|null Номер документа основания для коррекции (1179)
|
||||
*/
|
||||
protected ?string $number = null;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $type Тип коррекции
|
||||
* @param string $date Дата документа
|
||||
* @param string $number Номер документа
|
||||
* @throws InvalidEnumValueException
|
||||
* @throws InvalidCorrectionDateException
|
||||
* @throws EmptyCorrectionNumberException
|
||||
*/
|
||||
public function __construct(string $type, string $date, string $number)
|
||||
{
|
||||
$this->setType($type)->setDate($date)->setNumber($number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает тип коррекции
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип коррекции
|
||||
*
|
||||
* @param string $type
|
||||
* @return $this
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$type = trim($type);
|
||||
CorrectionTypes::isValid($type) && $this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает дату документа основания для коррекции
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDate(): ?string
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает дату документа основания для коррекции
|
||||
*
|
||||
* @param DateTime|string $date Строковая дата в формате d.m.Y либо объект DateTime с датой
|
||||
* @return $this
|
||||
* @throws InvalidCorrectionDateException
|
||||
*/
|
||||
public function setDate(DateTime|string $date): self
|
||||
{
|
||||
try {
|
||||
if (is_string($date)) {
|
||||
$date = new DateTime(trim($date));
|
||||
}
|
||||
$this->date = $date->format(Constraints::CORRECTION_DATE_FORMAT);
|
||||
} catch (Exception $e) {
|
||||
throw new InvalidCorrectionDateException($date, $e->getMessage());
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный номер документа основания для коррекции
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getNumber(): ?string
|
||||
{
|
||||
return $this->number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает номер документа основания для коррекции
|
||||
*
|
||||
* @param string $number
|
||||
* @return $this
|
||||
* @throws EmptyCorrectionNumberException
|
||||
*/
|
||||
public function setNumber(string $number): self
|
||||
{
|
||||
$number = trim($number);
|
||||
empty($number) && throw new EmptyCorrectionNumberException();
|
||||
$this->number = $number;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
#[ArrayShape(['type' => 'string', 'base_date' => 'string', 'base_number' => 'string'])]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'type' => $this->getType(),
|
||||
'base_date' => $this->getDate(),
|
||||
'base_number' => $this->getNumber(),
|
||||
];
|
||||
}
|
||||
}
|
93
src/Entities/Entity.php
Normal file
93
src/Entities/Entity.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
/** @noinspection PhpMultipleClassDeclarationsInspection */
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use ArrayAccess;
|
||||
use BadMethodCallException;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JsonSerializable;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Абстрактное описание любой сущности, представляемой как json
|
||||
*/
|
||||
abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function jsonSerialize(): array;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'company' => "\AtolOnline\Entities\Company",
|
||||
'correction_info' => "\AtolOnline\Entities\CorrectionInfo",
|
||||
'payments' => "array",
|
||||
'vats' => "\AtolOnline\Collections\Vats|null",
|
||||
'cashier' => "null|string",
|
||||
])]
|
||||
public function toArray()
|
||||
{
|
||||
return $this->jsonSerialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает строковое представление json-структуры объекта
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return isset($this->toArray()[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->toArray()[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetSet(mixed $offset, mixed $value)
|
||||
{
|
||||
throw new BadMethodCallException(
|
||||
'Объект ' . static::class . ' нельзя изменять как массив. Следует использовать сеттеры.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
throw new BadMethodCallException(
|
||||
'Объект ' . static::class . ' нельзя изменять как массив. Следует использовать сеттеры.'
|
||||
);
|
||||
}
|
||||
}
|
594
src/Entities/Item.php
Normal file
594
src/Entities/Item.php
Normal file
@ -0,0 +1,594 @@
|
||||
<?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\Enums\{
|
||||
PaymentMethods,
|
||||
PaymentObjects,
|
||||
VatTypes
|
||||
};
|
||||
use AtolOnline\Exceptions\{
|
||||
EmptyItemNameException,
|
||||
InvalidDeclarationNumberException,
|
||||
InvalidEnumValueException,
|
||||
InvalidOKSMCodeException,
|
||||
NegativeItemExciseException,
|
||||
NegativeItemPriceException,
|
||||
NegativeItemQuantityException,
|
||||
TooHighItemPriceException,
|
||||
TooHighItemQuantityException,
|
||||
TooHighItemSumException,
|
||||
TooLongItemCodeException,
|
||||
TooLongItemNameException,
|
||||
TooLongMeasurementUnitException,
|
||||
TooLongUserdataException,
|
||||
TooManyException
|
||||
};
|
||||
|
||||
/**
|
||||
* Предмет расчёта (товар, услуга)
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21-30
|
||||
*/
|
||||
final class Item extends Entity
|
||||
{
|
||||
/**
|
||||
* @var string Наименование (1030)
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* @var float Цена в рублях (с учётом скидок и наценок) (1079)
|
||||
*/
|
||||
protected float $price;
|
||||
|
||||
/**
|
||||
* @var float Количество/вес (1023)
|
||||
*/
|
||||
protected float $quantity;
|
||||
|
||||
/**
|
||||
* @var string|null Единица измерения (1197)
|
||||
*/
|
||||
protected ?string $measurement_unit = null;
|
||||
|
||||
/**
|
||||
* @var string|null Код товара (1162)
|
||||
*/
|
||||
protected ?string $code = null;
|
||||
|
||||
/**
|
||||
* @var string|null Код товара (1162) в форматированной шестнадцатиричной форме
|
||||
*/
|
||||
protected ?string $code_hex = null;
|
||||
|
||||
/**
|
||||
* @var string|null Признак способа расчёта (1214)
|
||||
*/
|
||||
protected ?string $payment_method = null;
|
||||
|
||||
/**
|
||||
* @var string|null Признак предмета расчёта (1212)
|
||||
*/
|
||||
protected ?string $payment_object = null;
|
||||
|
||||
/**
|
||||
* @var string|null Номер таможенной декларации (1321)
|
||||
*/
|
||||
protected ?string $declaration_number = null;
|
||||
|
||||
/**
|
||||
* @var Vat|null Ставка НДС
|
||||
*/
|
||||
protected ?Vat $vat = null;
|
||||
|
||||
/**
|
||||
* @var AgentInfo|null Атрибуты агента
|
||||
*/
|
||||
protected ?AgentInfo $agent_info = null;
|
||||
|
||||
/**
|
||||
* @var Supplier|null Атрибуты поставшика
|
||||
*/
|
||||
protected ?Supplier $supplier = null;
|
||||
|
||||
/**
|
||||
* @var string|null Дополнительный реквизит (1191)
|
||||
*/
|
||||
protected ?string $user_data = null;
|
||||
|
||||
/**
|
||||
* @var float|null Сумма акциза, включенная в стоимость (1229)
|
||||
*/
|
||||
protected ?float $excise = null;
|
||||
|
||||
/**
|
||||
* @var string|null Цифровой код страны происхождения товара (1230)
|
||||
*/
|
||||
protected ?string $country_code = null;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string|null $name Наименование
|
||||
* @param float|null $price Цена за одну единицу
|
||||
* @param float|null $quantity Количество
|
||||
* @throws TooLongItemNameException
|
||||
* @throws TooHighItemPriceException
|
||||
* @throws TooManyException
|
||||
* @throws NegativeItemPriceException
|
||||
* @throws EmptyItemNameException
|
||||
* @throws NegativeItemQuantityException
|
||||
*/
|
||||
public function __construct(
|
||||
string $name = null,
|
||||
float $price = null,
|
||||
float $quantity = null,
|
||||
) {
|
||||
!is_null($name) && $this->setName($name);
|
||||
!is_null($price) && $this->setPrice($price);
|
||||
!is_null($quantity) && $this->setQuantity($quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает наименование
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устаналивает наименование
|
||||
*
|
||||
* @param string $name Наименование
|
||||
* @return $this
|
||||
* @throws TooLongItemNameException
|
||||
* @throws EmptyItemNameException
|
||||
*/
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$name = trim($name);
|
||||
if (mb_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) {
|
||||
throw new TooLongItemNameException($name);
|
||||
}
|
||||
if (empty($name)) {
|
||||
throw new EmptyItemNameException();
|
||||
}
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает цену в рублях
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getPrice(): float
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает цену в рублях
|
||||
*
|
||||
* @param float $price
|
||||
* @return $this
|
||||
* @throws NegativeItemPriceException
|
||||
* @throws TooHighItemPriceException
|
||||
* @throws TooHighItemSumException
|
||||
*/
|
||||
public function setPrice(float $price): self
|
||||
{
|
||||
$price = round($price, 2);
|
||||
if ($price > Constraints::MAX_COUNT_ITEM_PRICE) {
|
||||
throw new TooHighItemPriceException($this->getName(), $price);
|
||||
}
|
||||
if ($price < 0) {
|
||||
throw new NegativeItemPriceException($this->getName(), $price);
|
||||
}
|
||||
$this->price = $price;
|
||||
$this->getVat()?->setSum($this->getSum());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает количество
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getQuantity(): float
|
||||
{
|
||||
return $this->quantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает количество
|
||||
*
|
||||
* @param float $quantity Количество
|
||||
* @return $this
|
||||
* @throws TooHighItemQuantityException
|
||||
* @throws NegativeItemQuantityException
|
||||
* @throws TooHighItemSumException
|
||||
*/
|
||||
public function setQuantity(float $quantity): self
|
||||
{
|
||||
$quantity = round($quantity, 3);
|
||||
if ($quantity > Constraints::MAX_COUNT_ITEM_QUANTITY) {
|
||||
throw new TooHighItemQuantityException($this->getName(), $quantity);
|
||||
}
|
||||
if ($quantity < 0) {
|
||||
throw new NegativeItemQuantityException($this->getName(), $quantity);
|
||||
}
|
||||
$this->quantity = $quantity;
|
||||
$this->getVat()?->setSum($this->getSum());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает стоимость (цена * количество + акциз)
|
||||
*
|
||||
* @return float
|
||||
* @throws TooHighItemSumException
|
||||
*/
|
||||
public function getSum(): float
|
||||
{
|
||||
$sum = $this->getPrice() * $this->getQuantity() + (float)$this->getExcise();
|
||||
if ($sum > Constraints::MAX_COUNT_ITEM_SUM) {
|
||||
throw new TooHighItemSumException($this->getName(), $sum);
|
||||
}
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает заданную единицу измерения количества
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMeasurementUnit(): ?string
|
||||
{
|
||||
return $this->measurement_unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает единицу измерения количества
|
||||
*
|
||||
* @param string|null $measurement_unit
|
||||
* @return $this
|
||||
* @throws TooLongMeasurementUnitException
|
||||
*/
|
||||
public function setMeasurementUnit(?string $measurement_unit): self
|
||||
{
|
||||
$measurement_unit = trim((string)$measurement_unit);
|
||||
if (mb_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) {
|
||||
throw new TooLongMeasurementUnitException($measurement_unit);
|
||||
}
|
||||
$this->measurement_unit = $measurement_unit ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный код товара
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает шестнадцатиричное представление кода товара
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCodeHex(): ?string
|
||||
{
|
||||
return $this->code_hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает код товара
|
||||
*
|
||||
* @param string|null $code
|
||||
* @return Item
|
||||
* @throws TooLongItemCodeException
|
||||
*/
|
||||
public function setCode(?string $code): self
|
||||
{
|
||||
$hex_string = null;
|
||||
$code = trim((string)$code);
|
||||
if (mb_strlen($code) > Constraints::MAX_LENGTH_ITEM_CODE) {
|
||||
throw new TooLongItemCodeException($this->getName(), $code);
|
||||
}
|
||||
if (!empty($code)) {
|
||||
$hex = bin2hex($code);
|
||||
$hex_string = trim(preg_replace('/([\dA-Fa-f]{2})/', '$1 ', $hex));
|
||||
}
|
||||
$this->code = $code ?: null;
|
||||
$this->code_hex = $hex_string ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает признак способа оплаты
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPaymentMethod(): ?string
|
||||
{
|
||||
return $this->payment_method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает признак способа оплаты
|
||||
*
|
||||
* @param string|null $payment_method Признак способа оплаты
|
||||
* @return $this
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setPaymentMethod(?string $payment_method): self
|
||||
{
|
||||
$payment_method = trim((string)$payment_method);
|
||||
PaymentMethods::isValid($payment_method);
|
||||
$this->payment_method = $payment_method ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает признак предмета расчёта
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPaymentObject(): ?string
|
||||
{
|
||||
return $this->payment_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает признак предмета расчёта
|
||||
*
|
||||
* @param string|null $payment_object Признак предмета расчёта
|
||||
* @return $this
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setPaymentObject(?string $payment_object): self
|
||||
{
|
||||
$payment_object = trim((string)$payment_object);
|
||||
PaymentObjects::isValid($payment_object);
|
||||
$this->payment_object = $payment_object ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает ставку НДС
|
||||
*
|
||||
* @return Vat|null
|
||||
*/
|
||||
public function getVat(): ?Vat
|
||||
{
|
||||
return $this->vat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает ставку НДС
|
||||
*
|
||||
* @param Vat|string|null $vat Объект ставки, одно из значений VatTypes или null для удаления ставки
|
||||
* @return $this
|
||||
* @throws TooHighItemSumException
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setVat(Vat|string|null $vat): self
|
||||
{
|
||||
if (is_string($vat)) {
|
||||
$vat = trim($vat);
|
||||
empty($vat)
|
||||
? $this->vat = null
|
||||
: VatTypes::isValid($vat) && $this->vat = new Vat($vat, $this->getSum());
|
||||
} elseif ($vat instanceof Vat) {
|
||||
$vat->setSum($this->getSum());
|
||||
$this->vat = $vat;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный объект атрибутов агента
|
||||
*
|
||||
* @return AgentInfo|null
|
||||
*/
|
||||
public function getAgentInfo(): ?AgentInfo
|
||||
{
|
||||
return $this->agent_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает атрибуты агента
|
||||
*
|
||||
* @param AgentInfo|null $agent_info
|
||||
* @return Item
|
||||
*/
|
||||
public function setAgentInfo(?AgentInfo $agent_info): self
|
||||
{
|
||||
$this->agent_info = $agent_info;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного поставщика
|
||||
*
|
||||
* @return Supplier|null
|
||||
*/
|
||||
public function getSupplier(): ?Supplier
|
||||
{
|
||||
return $this->supplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает поставщика
|
||||
*
|
||||
* @param Supplier|null $supplier
|
||||
* @return Item
|
||||
*/
|
||||
public function setSupplier(?Supplier $supplier): self
|
||||
{
|
||||
$this->supplier = $supplier;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает дополнительный реквизит
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUserData(): ?string
|
||||
{
|
||||
return $this->user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает дополнительный реквизит
|
||||
*
|
||||
* @param string|null $user_data Дополнительный реквизит
|
||||
* @return $this
|
||||
* @throws TooLongUserdataException
|
||||
*/
|
||||
public function setUserData(?string $user_data): self
|
||||
{
|
||||
$user_data = trim((string)$user_data);
|
||||
if (mb_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) {
|
||||
throw new TooLongUserdataException($user_data);
|
||||
}
|
||||
$this->user_data = $user_data ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную сумму акциза
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function getExcise(): ?float
|
||||
{
|
||||
return $this->excise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает сумму акциза
|
||||
*
|
||||
* @param float|null $excise
|
||||
* @return Item
|
||||
* @throws NegativeItemExciseException
|
||||
* @throws TooHighItemSumException
|
||||
*/
|
||||
public function setExcise(?float $excise): self
|
||||
{
|
||||
if ($excise < 0) {
|
||||
throw new NegativeItemExciseException($this->getName(), $excise);
|
||||
}
|
||||
$this->excise = $excise;
|
||||
$this->getVat()?->setSum($this->getSum());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный код страны происхождения товара
|
||||
*
|
||||
* @return string|null
|
||||
* @see https://ru.wikipedia.org/wiki/Общероссийский_классификатор_стран_мира
|
||||
* @see https://classifikators.ru/oksm
|
||||
*/
|
||||
public function getCountryCode(): ?string
|
||||
{
|
||||
return $this->country_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает код страны происхождения товара
|
||||
*
|
||||
* @param string|null $country_code
|
||||
* @return Item
|
||||
* @throws InvalidOKSMCodeException
|
||||
* @see https://classifikators.ru/oksm
|
||||
* @see https://ru.wikipedia.org/wiki/Общероссийский_классификатор_стран_мира
|
||||
*/
|
||||
public function setCountryCode(?string $country_code): self
|
||||
{
|
||||
$country_code = trim((string)$country_code);
|
||||
if (preg_match(Constraints::PATTERN_OKSM_CODE, $country_code) != 1) {
|
||||
throw new InvalidOKSMCodeException($country_code);
|
||||
}
|
||||
$this->country_code = $country_code ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный код таможенной декларации
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDeclarationNumber(): ?string
|
||||
{
|
||||
return $this->declaration_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает код таможенной декларации
|
||||
*
|
||||
* @param string|null $declaration_number
|
||||
* @return Item
|
||||
* @throws InvalidDeclarationNumberException
|
||||
*/
|
||||
public function setDeclarationNumber(?string $declaration_number): self
|
||||
{
|
||||
if (is_string($declaration_number)) {
|
||||
$declaration_number = trim($declaration_number);
|
||||
if (
|
||||
mb_strlen($declaration_number) < Constraints::MIN_LENGTH_DECLARATION_NUMBER
|
||||
|| mb_strlen($declaration_number) > Constraints::MAX_LENGTH_DECLARATION_NUMBER
|
||||
) {
|
||||
throw new InvalidDeclarationNumberException($declaration_number);
|
||||
}
|
||||
}
|
||||
$this->declaration_number = $declaration_number;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws TooHighItemSumException
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [
|
||||
'name' => $this->getName(),
|
||||
'price' => $this->getPrice(),
|
||||
'quantity' => $this->getQuantity(),
|
||||
'sum' => $this->getSum(),
|
||||
];
|
||||
!is_null($this->getMeasurementUnit()) && $json['measurement_unit'] = $this->getMeasurementUnit();
|
||||
!is_null($this->getCodeHex()) && $json['nomenclature_code'] = $this->getCodeHex();
|
||||
!is_null($this->getPaymentMethod()) && $json['payment_method'] = $this->getPaymentMethod();
|
||||
!is_null($this->getPaymentObject()) && $json['payment_object'] = $this->getPaymentObject();
|
||||
!is_null($this->getDeclarationNumber()) && $json['declaration_number'] = $this->getDeclarationNumber();
|
||||
$this->getVat()?->jsonSerialize() && $json['vat'] = $this->getVat()->jsonSerialize();
|
||||
$this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo()->jsonSerialize();
|
||||
$this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier()->jsonSerialize();
|
||||
!is_null($this->getUserData()) && $json['user_data'] = $this->getUserData();
|
||||
!is_null($this->getExcise()) && $json['excise'] = $this->getExcise();
|
||||
!is_null($this->getCountryCode()) && $json['country_code'] = $this->getCountryCode();
|
||||
return $json;
|
||||
}
|
||||
}
|
138
src/Entities/Kkt.php
Normal file
138
src/Entities/Kkt.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?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,
|
||||
NotEnoughMonitorDataException};
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Класс сущности ККТ, получаемой от монитора
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11
|
||||
* @property-read string|null serialNumber Заводской номер ККТ
|
||||
* @property-read string|null registrationNumber Регистрационный номер машины (РНМ)
|
||||
* @property-read string|null deviceNumber Номер автоматического устройства (внутренний идентификатор устройства)
|
||||
* @property-read DateTime|string|null fiscalizationDate Дата активации (фискализации) ФН с указанием таймзоны
|
||||
* @property-read DateTime|string|null fiscalStorageExpiration Дата замены ФН (Срок действия ФН), с указанием таймзоны
|
||||
* @property-read int|null signedDocuments Количество подписанных документов в ФН
|
||||
* @property-read float|null fiscalStoragePercentageUse Наполненость ФН в %
|
||||
* @property-read string|null fiscalStorageINN ИНН компании (указанный в ФН)
|
||||
* @property-read string|null fiscalStorageSerialNumber Заводской (серийный) номер ФН
|
||||
* @property-read string|null fiscalStoragePaymentAddress Адрес расчёта, указанный в ФН
|
||||
* @property-read string|null groupCode Код группы кассы
|
||||
* @property-read DateTime|string|null timestamp Время и дата формирования данных, UTC
|
||||
* @property-read bool|null isShiftOpened Признак открыта смена (true) или закрыта (false)
|
||||
* @property-read int|null shiftNumber Номер смены (или "Номер закрытой смены", когда смена закрыта)
|
||||
* @property-read int|null shiftReceipt Номер документа за смену (или "Кол-во чеков закрытой смены", когда смена
|
||||
* закрыта)
|
||||
* @property-read int|null unsentDocs Количество неотправленных документов. Указывается, если значение отлично от 0.
|
||||
* @property-read DateTime|string|null firstUnsetDocTimestamp Дата первого неотправленного документа. Указывается, если
|
||||
* есть неотправленные документы.
|
||||
* @property-read int|null networkErrorCode Код ошибки сети
|
||||
*/
|
||||
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 object $data)
|
||||
{
|
||||
if (empty((array)$data)) {
|
||||
throw new EmptyMonitorDataException();
|
||||
}
|
||||
$diff = array_diff($this->properties, array_keys((array)$data));
|
||||
if (count($diff) !== 0) {
|
||||
throw new NotEnoughMonitorDataException($diff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Эмулирует обращение к атрибутам
|
||||
*
|
||||
* @param string $name
|
||||
* @return null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
if (empty($this->data?->$name)) {
|
||||
return null;
|
||||
}
|
||||
if (in_array($name, $this->timestamps)) {
|
||||
return new DateTime($this->data->$name);
|
||||
}
|
||||
return $this->data->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return (array)$this->data;
|
||||
}
|
||||
}
|
133
src/Entities/MoneyTransferOperator.php
Normal file
133
src/Entities/MoneyTransferOperator.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?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\{
|
||||
InvalidInnLengthException,
|
||||
InvalidPhoneException
|
||||
};
|
||||
use AtolOnline\Traits\{
|
||||
HasInn,
|
||||
HasPhones
|
||||
};
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Класс, описывающий оператора перевода
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 28
|
||||
*/
|
||||
final class MoneyTransferOperator extends Entity
|
||||
{
|
||||
use HasInn, HasPhones;
|
||||
|
||||
/**
|
||||
* @var string|null Наименование (1026)
|
||||
*/
|
||||
protected ?string $name = null;
|
||||
|
||||
/**
|
||||
* @var string|null ИНН (1016)
|
||||
*/
|
||||
protected ?string $inn = null;
|
||||
|
||||
/**
|
||||
* @var string|null Адрес (1005)
|
||||
*/
|
||||
protected ?string $address = null;
|
||||
|
||||
/**
|
||||
* @var Collection Телефоны (1075)
|
||||
*/
|
||||
protected Collection $phones;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string|null $name Наименование поставщика (1225)
|
||||
* @param string|null $inn ИНН (1226)
|
||||
* @param string|null $address Адрес (1005)
|
||||
* @param array|Collection|null $phones Телефоны поставщика (1171)
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPhoneException
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $inn = null,
|
||||
?string $address = null,
|
||||
array|Collection|null $phones = null,
|
||||
) {
|
||||
$this->setName($name);
|
||||
$this->setInn($inn);
|
||||
$this->setAddress($address);
|
||||
$this->setPhones($phones);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленное наименование поставщика
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает наименование поставщика
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setName(?string $name): self
|
||||
{
|
||||
// критерии валидной строки не описаны ни в схеме, ни в документации
|
||||
$this->name = trim((string)$name) ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный адрес места расчётов
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAddress(): ?string
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает адрес места расчётов
|
||||
*
|
||||
* @param string|null $address
|
||||
* @return $this
|
||||
*/
|
||||
public function setAddress(?string $address): self
|
||||
{
|
||||
// критерии валидной строки не описаны ни в схеме, ни в документации
|
||||
$this->address = trim((string)$address) ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [];
|
||||
$this->getName() && $json['name'] = $this->getName();
|
||||
$this->getInn() && $json['inn'] = $this->getInn();
|
||||
$this->getAddress() && $json['address'] = $this->getAddress();
|
||||
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
|
||||
return $json;
|
||||
}
|
||||
}
|
90
src/Entities/PayingAgent.php
Normal file
90
src/Entities/PayingAgent.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Exceptions\{
|
||||
InvalidPhoneException,
|
||||
TooLongPayingAgentOperationException};
|
||||
use AtolOnline\Traits\HasPhones;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Класс, описывающий платёжного агента
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19
|
||||
*/
|
||||
final class PayingAgent extends Entity
|
||||
{
|
||||
use HasPhones;
|
||||
|
||||
/**
|
||||
* @var string|null Наименование операции (1044)
|
||||
*/
|
||||
protected ?string $operation = null;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string|null $operation Наименование операции (1044)
|
||||
* @param array|Collection|null $phones Телефоны платёжного агента (1073)
|
||||
* @throws TooLongPayingAgentOperationException
|
||||
* @throws InvalidPhoneException
|
||||
*/
|
||||
public function __construct(
|
||||
?string $operation = null,
|
||||
array|Collection|null $phones = null,
|
||||
) {
|
||||
!is_null($operation) && $this->setOperation($operation);
|
||||
$this->setPhones($phones);
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает операцию
|
||||
*
|
||||
* @param string|null $operation
|
||||
* @return $this
|
||||
* @throws TooLongPayingAgentOperationException
|
||||
*/
|
||||
public function setOperation(?string $operation): self
|
||||
{
|
||||
if (!is_null($operation)) {
|
||||
$operation = trim($operation);
|
||||
if (mb_strlen($operation) > Constraints::MAX_LENGTH_PAYING_AGENT_OPERATION) {
|
||||
throw new TooLongPayingAgentOperationException($operation);
|
||||
}
|
||||
}
|
||||
$this->operation = $operation ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вoзвращает установленную операцию
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOperation(): ?string
|
||||
{
|
||||
return $this->operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [];
|
||||
$this->getOperation() && $json['operation'] = $this->getOperation();
|
||||
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
|
||||
return $json;
|
||||
}
|
||||
}
|
125
src/Entities/Payment.php
Normal file
125
src/Entities/Payment.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\{
|
||||
Constants\Constraints,
|
||||
Enums\PaymentTypes,
|
||||
};
|
||||
use AtolOnline\Exceptions\{
|
||||
InvalidEnumValueException,
|
||||
NegativePaymentSumException,
|
||||
TooHighPaymentSumException,
|
||||
};
|
||||
use JetBrains\PhpStorm\{
|
||||
ArrayShape,
|
||||
Pure
|
||||
};
|
||||
|
||||
/**
|
||||
* Класс, описывающий оплату
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30
|
||||
*/
|
||||
final class Payment extends Entity
|
||||
{
|
||||
/**
|
||||
* @var int Тип оплаты
|
||||
*/
|
||||
protected int $type;
|
||||
|
||||
/**
|
||||
* @var float Сумма оплаты (1031, 1081, 1215, 1216, 1217)
|
||||
*/
|
||||
protected float $sum;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param int $type Тип оплаты
|
||||
* @param float $sum Сумма оплаты
|
||||
* @throws NegativePaymentSumException
|
||||
* @throws TooHighPaymentSumException
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function __construct(int $type, float $sum)
|
||||
{
|
||||
$this->setType($type)->setSum($sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный тип оплаты
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getType(): int
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип оплаты
|
||||
*
|
||||
* @param int $type
|
||||
* @return $this
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setType(int $type): self
|
||||
{
|
||||
PaymentTypes::isValid($type) && $this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную сумму оплаты
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSum(): float
|
||||
{
|
||||
return $this->sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает сумму оплаты
|
||||
*
|
||||
* @param float $sum
|
||||
* @return $this
|
||||
* @throws TooHighPaymentSumException
|
||||
* @throws NegativePaymentSumException
|
||||
*/
|
||||
public function setSum(float $sum): self
|
||||
{
|
||||
$sum = round($sum, 2);
|
||||
if ($sum > Constraints::MAX_COUNT_PAYMENT_SUM) {
|
||||
throw new TooHighPaymentSumException($sum);
|
||||
}
|
||||
if ($sum < 0) {
|
||||
throw new NegativePaymentSumException($sum);
|
||||
}
|
||||
$this->sum = $sum;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
#[ArrayShape(['type' => 'int', 'sum' => 'float'])]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'type' => $this->getType(),
|
||||
'sum' => $this->getSum(),
|
||||
];
|
||||
}
|
||||
}
|
480
src/Entities/Receipt.php
Normal file
480
src/Entities/Receipt.php
Normal file
@ -0,0 +1,480 @@
|
||||
<?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\Api\AtolResponse;
|
||||
use AtolOnline\Api\Fiscalizer;
|
||||
use AtolOnline\Collections\Items;
|
||||
use AtolOnline\Collections\Payments;
|
||||
use AtolOnline\Collections\Vats;
|
||||
use AtolOnline\Constants\Constraints;
|
||||
use AtolOnline\Exceptions\AuthFailedException;
|
||||
use AtolOnline\Exceptions\EmptyItemsException;
|
||||
use AtolOnline\Exceptions\EmptyLoginException;
|
||||
use AtolOnline\Exceptions\EmptyPasswordException;
|
||||
use AtolOnline\Exceptions\InvalidEntityInCollectionException;
|
||||
use AtolOnline\Exceptions\InvalidInnLengthException;
|
||||
use AtolOnline\Exceptions\InvalidPaymentAddressException;
|
||||
use AtolOnline\Exceptions\TooLongAddCheckPropException;
|
||||
use AtolOnline\Exceptions\TooLongCashierException;
|
||||
use AtolOnline\Exceptions\TooLongPaymentAddressException;
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
|
||||
/**
|
||||
* Класс, описывающий документ прихода, расхода, возврата прихода, возврата расхода
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17
|
||||
*/
|
||||
final class Receipt extends Entity
|
||||
{
|
||||
/**
|
||||
* Тип документа
|
||||
*/
|
||||
public const DOC_TYPE = 'receipt';
|
||||
|
||||
/**
|
||||
* @var Client Покупатель
|
||||
*/
|
||||
protected Client $client;
|
||||
|
||||
/**
|
||||
* @todo вынести в трейт?
|
||||
* @var Company Продавец
|
||||
*/
|
||||
protected Company $company;
|
||||
|
||||
/**
|
||||
* @var AgentInfo|null Агент
|
||||
*/
|
||||
protected ?AgentInfo $agent_info = null;
|
||||
|
||||
/**
|
||||
* @var Supplier|null Поставщик
|
||||
*/
|
||||
protected ?Supplier $supplier = null;
|
||||
|
||||
/**
|
||||
* @var Items Коллекция предметов расчёта
|
||||
*/
|
||||
protected Items $items;
|
||||
|
||||
/**
|
||||
* @todo вынести в трейт?
|
||||
* @var Payments Коллекция оплат
|
||||
*/
|
||||
protected Payments $payments;
|
||||
|
||||
/**
|
||||
* @var Vats|null Коллекция ставок НДС
|
||||
*/
|
||||
protected ?Vats $vats = null;
|
||||
|
||||
/**
|
||||
* @var float Итоговая сумма чека
|
||||
*/
|
||||
protected float $total = 0;
|
||||
|
||||
/**
|
||||
* @todo вынести в трейт?
|
||||
* @var string|null ФИО кассира
|
||||
*/
|
||||
protected ?string $cashier = null;
|
||||
|
||||
/**
|
||||
* @var string|null Дополнительный реквизит
|
||||
*/
|
||||
protected ?string $add_check_props = null;
|
||||
|
||||
/**
|
||||
* @var AdditionalUserProps|null Дополнительный реквизит пользователя
|
||||
*/
|
||||
protected ?AdditionalUserProps $add_user_props = null;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param Client $client
|
||||
* @param Company $company
|
||||
* @param Items $items
|
||||
* @param Payments $payments
|
||||
* @throws EmptyItemsException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
public function __construct(Client $client, Company $company, Items $items, Payments $payments)
|
||||
{
|
||||
$this->setClient($client)->setCompany($company)->setItems($items)->setPayments($payments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного покупателя
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function getClient(): Client
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает покупателя
|
||||
*
|
||||
* @param Client $client
|
||||
* @return $this
|
||||
*/
|
||||
public function setClient(Client $client): self
|
||||
{
|
||||
$this->client = $client;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного продавца
|
||||
*
|
||||
* @return Company
|
||||
*/
|
||||
public function getCompany(): Company
|
||||
{
|
||||
return $this->company;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает продавца
|
||||
*
|
||||
* @param Company $company
|
||||
* @return $this
|
||||
*/
|
||||
public function setCompany(Company $company): self
|
||||
{
|
||||
$this->company = $company;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного агента
|
||||
*
|
||||
* @return AgentInfo|null
|
||||
*/
|
||||
public function getAgentInfo(): ?AgentInfo
|
||||
{
|
||||
return $this->agent_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает агента
|
||||
*
|
||||
* @param AgentInfo|null $agent_info
|
||||
* @return $this
|
||||
*/
|
||||
public function setAgentInfo(?AgentInfo $agent_info): self
|
||||
{
|
||||
$this->agent_info = $agent_info;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поставщика
|
||||
*
|
||||
* @return Supplier|null
|
||||
*/
|
||||
public function getSupplier(): ?Supplier
|
||||
{
|
||||
return $this->supplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поставщика
|
||||
*
|
||||
* @param Supplier|null $supplier
|
||||
* @return $this
|
||||
*/
|
||||
public function setSupplier(?Supplier $supplier): self
|
||||
{
|
||||
$this->supplier = $supplier;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную коллекцию предметов расчёта
|
||||
*
|
||||
* @return Items
|
||||
*/
|
||||
public function getItems(): Items
|
||||
{
|
||||
return $this->items ?? new Items();
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает коллекцию предметов расчёта
|
||||
*
|
||||
* @param Items $items
|
||||
* @return $this
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws Exception
|
||||
* @throws EmptyItemsException
|
||||
*/
|
||||
public function setItems(Items $items): self
|
||||
{
|
||||
$items->checkCount();
|
||||
$items->checkItemsClasses();
|
||||
$this->items = $items;
|
||||
$this->getItems()->each(fn($item) => $this->total += $item->getSum());
|
||||
$this->total = round($this->total, 2);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную коллекцию оплат
|
||||
*
|
||||
* @return Payments
|
||||
*/
|
||||
public function getPayments(): Payments
|
||||
{
|
||||
return $this->payments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает коллекцию оплат
|
||||
*
|
||||
* @param Payments $payments
|
||||
* @return $this
|
||||
* @throws InvalidEntityInCollectionException
|
||||
*/
|
||||
public function setPayments(Payments $payments): self
|
||||
{
|
||||
$payments->checkCount();
|
||||
$payments->checkItemsClasses();
|
||||
$this->payments = $payments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленную коллекцию ставок НДС
|
||||
*
|
||||
* @return Vats|null
|
||||
*/
|
||||
public function getVats(): ?Vats
|
||||
{
|
||||
return $this->vats ?? new Vats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает коллекцию ставок НДС
|
||||
*
|
||||
* @param Vats|null $vats
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setVats(?Vats $vats): self
|
||||
{
|
||||
$vats->checkCount();
|
||||
$vats->checkItemsClasses();
|
||||
$this->vats = $vats;
|
||||
/** @var Vat $vat */
|
||||
$this->getVats()->each(fn($vat) => $vat->setSum($this->getTotal()));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает полную сумму чека
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTotal(): float
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленного кассира
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCashier(): ?string
|
||||
{
|
||||
return $this->cashier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает кассира
|
||||
*
|
||||
* @param string|null $cashier
|
||||
* @return $this
|
||||
* @throws TooLongCashierException
|
||||
*/
|
||||
public function setCashier(?string $cashier): self
|
||||
{
|
||||
if (is_string($cashier)) {
|
||||
$cashier = trim($cashier);
|
||||
if (mb_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
|
||||
throw new TooLongCashierException($cashier);
|
||||
}
|
||||
}
|
||||
$this->cashier = $cashier ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный дополнительный реквизит чека
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAddCheckProps(): ?string
|
||||
{
|
||||
return $this->add_check_props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает дополнительный реквизит чека
|
||||
*
|
||||
* @param string|null $add_check_props
|
||||
* @return $this
|
||||
* @throws TooLongAddCheckPropException
|
||||
*/
|
||||
public function setAddCheckProps(?string $add_check_props): self
|
||||
{
|
||||
if (is_string($add_check_props)) {
|
||||
$add_check_props = trim($add_check_props);
|
||||
if (mb_strlen($add_check_props) > Constraints::MAX_LENGTH_ADD_CHECK_PROP) {
|
||||
throw new TooLongAddCheckPropException($add_check_props);
|
||||
}
|
||||
}
|
||||
$this->add_check_props = $add_check_props ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленный дополнительный реквизит пользователя
|
||||
*
|
||||
* @return AdditionalUserProps|null
|
||||
*/
|
||||
public function getAddUserProps(): ?AdditionalUserProps
|
||||
{
|
||||
return $this->add_user_props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанаваливает дополнительный реквизит пользователя
|
||||
*
|
||||
* @param AdditionalUserProps|null $add_user_props
|
||||
* @return $this
|
||||
*/
|
||||
public function setAddUserProps(?AdditionalUserProps $add_user_props): self
|
||||
{
|
||||
$this->add_user_props = $add_user_props;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует приход по текущему документу
|
||||
*
|
||||
* @param Fiscalizer $fiscalizer Объект фискализатора
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function sell(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $fiscalizer->sell($this, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует возврат прихода по текущему документу
|
||||
*
|
||||
* @param Fiscalizer $fiscalizer Объект фискализатора
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function sellRefund(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $fiscalizer->sellRefund($this, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует расход по текущему документу
|
||||
*
|
||||
* @param Fiscalizer $fiscalizer Объект фискализатора
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function buy(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $fiscalizer->buy($this, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует возврат расхода по текущему документу
|
||||
*
|
||||
* @param Fiscalizer $fiscalizer Объект фискализатора
|
||||
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID)
|
||||
* @return AtolResponse|null
|
||||
* @throws AuthFailedException
|
||||
* @throws EmptyLoginException
|
||||
* @throws EmptyPasswordException
|
||||
* @throws GuzzleException
|
||||
* @throws InvalidEntityInCollectionException
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPaymentAddressException
|
||||
* @throws TooLongPaymentAddressException
|
||||
*/
|
||||
public function buyRefund(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse
|
||||
{
|
||||
return $fiscalizer->buyRefund($this, $external_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает массив для кодирования в json
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [
|
||||
'client' => $this->getClient()->jsonSerialize(),
|
||||
'company' => $this->getCompany()->jsonSerialize(),
|
||||
'items' => $this->getItems()->jsonSerialize(),
|
||||
'total' => $this->getTotal(),
|
||||
'payments' => $this->getPayments()->jsonSerialize(),
|
||||
];
|
||||
$this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo()->jsonSerialize();
|
||||
$this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier()->jsonSerialize();
|
||||
$this->getVats()?->isNotEmpty() && $json['vats'] = $this->getVats();
|
||||
!is_null($this->getAddCheckProps()) && $json['additional_check_props'] = $this->getAddCheckProps();
|
||||
!is_null($this->getCashier()) && $json['cashier'] = $this->getCashier();
|
||||
$this->getAddUserProps()?->jsonSerialize() &&
|
||||
$json['additional_user_props'] = $this->getAddUserProps()->jsonSerialize();
|
||||
return $json;
|
||||
}
|
||||
}
|
47
src/Entities/ReceivePaymentsOperator.php
Normal file
47
src/Entities/ReceivePaymentsOperator.php
Normal 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\Entities;
|
||||
|
||||
use AtolOnline\Exceptions\InvalidPhoneException;
|
||||
use AtolOnline\Traits\HasPhones;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Класс, описывающий оператора по приёму платежей
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19-20
|
||||
*/
|
||||
final class ReceivePaymentsOperator extends Entity
|
||||
{
|
||||
use HasPhones;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param array|Collection|null $phones Телефоны оператора по приёму платежей (1074)
|
||||
* @throws InvalidPhoneException
|
||||
*/
|
||||
public function __construct(array|Collection|null $phones = null)
|
||||
{
|
||||
$this->setPhones($phones);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [];
|
||||
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
|
||||
return $json;
|
||||
}
|
||||
}
|
91
src/Entities/Supplier.php
Normal file
91
src/Entities/Supplier.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?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\{
|
||||
InvalidInnLengthException,
|
||||
InvalidPhoneException
|
||||
};
|
||||
use AtolOnline\Traits\{
|
||||
HasInn,
|
||||
HasPhones
|
||||
};
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Класс, описывающий поставшика
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29
|
||||
*/
|
||||
final class Supplier extends Entity
|
||||
{
|
||||
use HasPhones, HasInn;
|
||||
|
||||
/**
|
||||
* @var string|null Наименование (1225)
|
||||
*/
|
||||
protected ?string $name = null;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string|null $name Наименование поставщика (1225)
|
||||
* @param string|null $inn ИНН (1226)
|
||||
* @param array|Collection|null $phones Телефоны поставщика (1171)
|
||||
* @throws InvalidInnLengthException
|
||||
* @throws InvalidPhoneException
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $inn = null,
|
||||
array|Collection|null $phones = null,
|
||||
) {
|
||||
!is_null($name) && $this->setName($name);
|
||||
!is_null($inn) && $this->setInn($inn);
|
||||
$this->setPhones($phones);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает установленное наименование поставщика
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает наименование поставщика
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return Supplier
|
||||
*/
|
||||
public function setName(?string $name): self
|
||||
{
|
||||
// критерии к длине строки не описаны ни в схеме, ни в документации
|
||||
$this->name = trim($name) ?: null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$json = [];
|
||||
$this->getName() && $json['name'] = $this->getName();
|
||||
$this->getInn() && $json['inn'] = $this->getInn();
|
||||
!$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray();
|
||||
return $json;
|
||||
}
|
||||
}
|
146
src/Entities/Vat.php
Normal file
146
src/Entities/Vat.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Entities;
|
||||
|
||||
use AtolOnline\Enums\VatTypes;
|
||||
use AtolOnline\Exceptions\InvalidEnumValueException;
|
||||
use AtolOnline\Helpers;
|
||||
use JetBrains\PhpStorm\{
|
||||
ArrayShape,
|
||||
Pure};
|
||||
|
||||
/**
|
||||
* Класс, описывающий ставку НДС
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25, 31
|
||||
*/
|
||||
final class Vat extends Entity
|
||||
{
|
||||
/**
|
||||
* @var string Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106)
|
||||
*/
|
||||
private string $type;
|
||||
|
||||
/**
|
||||
* @var float Сумма в рублях, от которой пересчитывается размер НДС
|
||||
*/
|
||||
private float $sum;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $type Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106)
|
||||
* @param float $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function __construct(string $type, float $rubles)
|
||||
{
|
||||
$this->setType($type)->setSum($rubles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тип ставки НДС
|
||||
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
|
||||
*
|
||||
* @param string $type Тип ставки НДС
|
||||
* @return $this
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$type = trim($type);
|
||||
VatTypes::isValid($type) && $this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает тип ставки НДС
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает исходную сумму, от которой расчитывается размер налога
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSum(): float
|
||||
{
|
||||
return $this->sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает исходную сумму, от которой будет расчитываться итоговый размер НДС.
|
||||
* Автоматически пересчитывает итоговый размер НДС от исходной суммы.
|
||||
*
|
||||
* @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС
|
||||
* @return $this
|
||||
*/
|
||||
public function setSum(float $rubles): self
|
||||
{
|
||||
$this->sum = round($rubles, 2);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает расчитанный итоговый размер ставки НДС в рублях
|
||||
*
|
||||
* @return float
|
||||
* @see https://nalog-nalog.ru/nds/nalogovaya_baza_nds/kak-schitat-nds-pravilno-vychislyaem-20-ot-summy-primer-algoritm/
|
||||
* @see https://glavkniga.ru/situations/k500734
|
||||
* @see https://www.b-kontur.ru/nds-kalkuljator-online
|
||||
*/
|
||||
#[Pure]
|
||||
public function getCalculated(): float
|
||||
{
|
||||
return Helpers::toRub(
|
||||
match ($this->getType()) {
|
||||
VatTypes::VAT10 => Helpers::toKop($this->sum) * 10 / 100,
|
||||
VatTypes::VAT18 => Helpers::toKop($this->sum) * 18 / 100,
|
||||
VatTypes::VAT20 => Helpers::toKop($this->sum) * 20 / 100,
|
||||
VatTypes::VAT110 => Helpers::toKop($this->sum) * 10 / 110,
|
||||
VatTypes::VAT118 => Helpers::toKop($this->sum) * 18 / 118,
|
||||
VatTypes::VAT120 => Helpers::toKop($this->sum) * 20 / 120,
|
||||
default => 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Прибавляет указанную сумму к исходной
|
||||
*
|
||||
* @param float $rubles
|
||||
* @return $this
|
||||
*/
|
||||
public function addSum(float $rubles): self
|
||||
{
|
||||
$this->sum += $rubles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
#[Pure]
|
||||
#[ArrayShape(['type' => 'string', 'sum' => 'float'])]
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'type' => $this->getType(),
|
||||
'sum' => $this->getCalculated(),
|
||||
];
|
||||
}
|
||||
}
|
65
src/Enums/AgentTypes.php
Normal file
65
src/Enums/AgentTypes.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Enums;
|
||||
|
||||
use AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие типы агента
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 18, 26
|
||||
*/
|
||||
final class AgentTypes extends Enum
|
||||
{
|
||||
/**
|
||||
* Банковский платёжный агент
|
||||
*/
|
||||
const BANK_PAYING_AGENT = 'bank_paying_agent';
|
||||
|
||||
/**
|
||||
* Банковский платёжный субагент
|
||||
*/
|
||||
const BANK_PAYING_SUBAGENT = 'bank_paying_subagent';
|
||||
|
||||
/**
|
||||
* Платёжный агент
|
||||
*/
|
||||
const PAYING_AGENT = 'paying_agent';
|
||||
|
||||
/**
|
||||
* Платёжный субагент
|
||||
*/
|
||||
const PAYING_SUBAGENT = 'paying_subagent';
|
||||
|
||||
/**
|
||||
* Поверенный
|
||||
*/
|
||||
const ATTRONEY = 'attorney';
|
||||
|
||||
/**
|
||||
* Комиссионер
|
||||
*/
|
||||
const COMMISSION_AGENT = 'commission_agent';
|
||||
|
||||
/**
|
||||
* Другой тип агента
|
||||
*/
|
||||
const ANOTHER = 'another';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [Ffd105Tags::AGENT_TYPE];
|
||||
}
|
||||
}
|
@ -1,28 +1,40 @@
|
||||
<?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 AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие типы документов коррекции
|
||||
*
|
||||
* @package AtolOnline\Constants
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
|
||||
*/
|
||||
class CorrectionTypes
|
||||
final class CorrectionTypes extends Enum
|
||||
{
|
||||
/**
|
||||
* Самостоятельно
|
||||
*/
|
||||
const SELF = 'self';
|
||||
|
||||
|
||||
/**
|
||||
* По предписанию
|
||||
*/
|
||||
const INSTRUCTION = 'instruction';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [Ffd105Tags::CORRECTION_TYPE];
|
||||
}
|
||||
}
|
36
src/Enums/Enum.php
Normal file
36
src/Enums/Enum.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Enums;
|
||||
|
||||
use AtolOnline\Exceptions\InvalidEnumValueException;
|
||||
|
||||
/**
|
||||
* Расширение класса перечисления
|
||||
*/
|
||||
abstract class Enum extends \MyCLabs\Enum\Enum
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws InvalidEnumValueException
|
||||
*/
|
||||
public static function isValid($value)
|
||||
{
|
||||
return parent::isValid($value) ?: throw new InvalidEnumValueException(static::class, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает массив тегов ФФД
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
abstract public static function getFfdTags(): array;
|
||||
}
|
@ -1,54 +1,65 @@
|
||||
<?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 AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие признаки способов расчёта. Тег ФФД - 1214.
|
||||
* Константы, определяющие признаки способов расчёта
|
||||
*
|
||||
* @package AtolOnline\Constants
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 22
|
||||
*/
|
||||
class PaymentMethods
|
||||
final class PaymentMethods extends Enum
|
||||
{
|
||||
/**
|
||||
* Предоплата 100% до передачи предмета расчёта
|
||||
*/
|
||||
const FULL_PREPAYMENT = 'full_prepayment';
|
||||
|
||||
|
||||
/**
|
||||
* Частичная предоплата до передачи предмета расчёта
|
||||
*/
|
||||
const PREPAYMENT = 'prepayment';
|
||||
|
||||
|
||||
/**
|
||||
* Аванс
|
||||
*/
|
||||
const ADVANCE = 'advance';
|
||||
|
||||
|
||||
/**
|
||||
* Полная оплата с учётом аванса/предоплаты в момент передачи предмета расчёта
|
||||
*/
|
||||
const FULL_PAYMENT = 'full_payment';
|
||||
|
||||
|
||||
/**
|
||||
* Частичный расчёт в момент передачи предмета расчёта (дальнейшая оплата в кредит)
|
||||
*/
|
||||
const PARTIAL_PAYMENT = 'partial_payment';
|
||||
|
||||
|
||||
/**
|
||||
* Передача предмета расчёта в кредит
|
||||
*/
|
||||
const CREDIT = 'credit';
|
||||
|
||||
|
||||
/**
|
||||
* Оплата кредита
|
||||
*/
|
||||
const CREDIT_PAYMENT = 'credit_payment';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [Ffd105Tags::ITEM_PAYMENT_METHOD];
|
||||
}
|
||||
}
|
@ -1,108 +1,168 @@
|
||||
<?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 AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие признаки предметов расчёта. Тег ФФД - 1212.
|
||||
* Константы, определяющие признаки предметов расчёта
|
||||
*
|
||||
* @package AtolOnline\Constants
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 23
|
||||
*/
|
||||
class PaymentObjects
|
||||
final class PaymentObjects extends Enum
|
||||
{
|
||||
/**
|
||||
* Товар, кроме подакцизного
|
||||
*/
|
||||
const COMMODITY = 'commodity';
|
||||
|
||||
|
||||
/**
|
||||
* Товар подакцизный
|
||||
*/
|
||||
const EXCISE = 'excise';
|
||||
|
||||
|
||||
/**
|
||||
* Работа
|
||||
*/
|
||||
const JOB = 'job';
|
||||
|
||||
|
||||
/**
|
||||
* Услуга
|
||||
*/
|
||||
const SERVICE = 'service';
|
||||
|
||||
|
||||
/**
|
||||
* Ставка азартной игры
|
||||
*/
|
||||
const GAMBLING_BET = 'gambling_bet';
|
||||
|
||||
|
||||
/**
|
||||
* Выигрыш азартной игры
|
||||
*/
|
||||
const GAMBLING_PRIZE = 'gambling_prize';
|
||||
|
||||
|
||||
/**
|
||||
* Лотерея
|
||||
*/
|
||||
const LOTTERY = 'lottery';
|
||||
|
||||
|
||||
/**
|
||||
* Выигрыш лотереи
|
||||
*/
|
||||
const LOTTERY_PRIZE = 'lottery_prize';
|
||||
|
||||
|
||||
/**
|
||||
* Предоставление результатов интеллектуальной деятельности
|
||||
*/
|
||||
const INTELLECTUAL_ACTIVITY = 'intellectual_activity';
|
||||
|
||||
|
||||
/**
|
||||
* Платёж (задаток, кредит, аванс, предоплата, пеня, штраф, бонус и пр.)
|
||||
*/
|
||||
const PAYMENT = 'payment';
|
||||
|
||||
|
||||
/**
|
||||
* Агентское вознаграждение
|
||||
*/
|
||||
const AGENT_COMMISSION = 'agent_commission';
|
||||
|
||||
|
||||
/**
|
||||
* Составной предмет расчёта
|
||||
*
|
||||
* @deprecated Более не используется согласно ФФД 1.05
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25 (payment_object)
|
||||
*/
|
||||
const COMPOSITE = 'composite';
|
||||
|
||||
|
||||
/**
|
||||
* Другой предмет расчёта
|
||||
*/
|
||||
const ANOTHER = 'another';
|
||||
|
||||
|
||||
/**
|
||||
* Имущественное право
|
||||
*/
|
||||
const PROPERTY_RIGHT = 'property_right';
|
||||
|
||||
|
||||
/**
|
||||
* Внереализационный доход
|
||||
*/
|
||||
const NON_OPERATING_GAIN = 'non-operating_gain';
|
||||
|
||||
|
||||
/**
|
||||
* Страховые взносы
|
||||
*/
|
||||
const INSURANCE_PREMIUM = 'insurance_premium';
|
||||
|
||||
|
||||
/**
|
||||
* Торговый сбор
|
||||
*/
|
||||
const SALES_TAX = 'sales_tax';
|
||||
|
||||
|
||||
/**
|
||||
* Курортный сбор
|
||||
*/
|
||||
const RESORT_FEE = 'resort_fee';
|
||||
|
||||
/**
|
||||
* Взнос в счёт оплаты пени, штрафе, вознаграждении, бонусе и ином аналогичном предмете расчёта
|
||||
*/
|
||||
const AWARD = 'award';
|
||||
|
||||
/**
|
||||
* Залог
|
||||
*/
|
||||
const DEPOSIT = 'deposit';
|
||||
|
||||
/**
|
||||
* Расход, уменьшающий доход (в соответствии со статьей 346.16 НК РФ)
|
||||
*/
|
||||
const EXPENSE = 'expense';
|
||||
|
||||
/**
|
||||
* Взнос на ОПС ИП
|
||||
*/
|
||||
const PEN_INSURANCE_IP = 'pension_insurance_ip';
|
||||
|
||||
/**
|
||||
* Взнос на ОПС
|
||||
*/
|
||||
const PEN_INSURANCE = 'pension_insurance';
|
||||
|
||||
/**
|
||||
* Взнос на ОМС ИП
|
||||
*/
|
||||
const MED_INSURANCE_IP = 'medical_insurance_ip';
|
||||
|
||||
/**
|
||||
* Взнос на ОМС
|
||||
*/
|
||||
const MED_INSURANCE = 'medical_insurance';
|
||||
|
||||
/**
|
||||
* Взнос на ОСС
|
||||
*/
|
||||
const SOC_INSURANCE = 'social_insurance';
|
||||
|
||||
/**
|
||||
* Платёж казино
|
||||
*/
|
||||
const CASINO_PAYMENT = 'casino_payment';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [Ffd105Tags::ITEM_PAYMENT_OBJECT];
|
||||
}
|
||||
}
|
91
src/Enums/PaymentTypes.php
Normal file
91
src/Enums/PaymentTypes.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Антон Аксенов (Anthony Axenov)
|
||||
*
|
||||
* This code is licensed under MIT.
|
||||
* Этот код распространяется по лицензии MIT.
|
||||
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace AtolOnline\Enums;
|
||||
|
||||
use AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие виды оплат
|
||||
*
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
|
||||
*/
|
||||
final class PaymentTypes extends Enum
|
||||
{
|
||||
/**
|
||||
* Расчёт наличными
|
||||
*/
|
||||
const CASH = 0;
|
||||
|
||||
/**
|
||||
* Расчёт безналичными
|
||||
*/
|
||||
const ELECTRON = 1;
|
||||
|
||||
/**
|
||||
* Предварительная оплата (зачёт аванса)
|
||||
*/
|
||||
const PREPAID = 2;
|
||||
|
||||
/**
|
||||
* Предварительная оплата (кредит)
|
||||
*/
|
||||
const CREDIT = 3;
|
||||
|
||||
/**
|
||||
* Иная форма оплаты (встречное предоставление)
|
||||
*/
|
||||
const OTHER = 4;
|
||||
|
||||
/**
|
||||
* Расширенный типы оплаты (5)
|
||||
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
|
||||
*/
|
||||
const ADD_5 = 5;
|
||||
|
||||
/**
|
||||
* Расширенный типы оплаты (6)
|
||||
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
|
||||
*/
|
||||
const ADD_6 = 6;
|
||||
|
||||
/**
|
||||
* Расширенный типы оплаты (7)
|
||||
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
|
||||
*/
|
||||
const ADD_7 = 7;
|
||||
|
||||
/**
|
||||
* Расширенный типы оплаты (8)
|
||||
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
|
||||
*/
|
||||
const ADD_8 = 8;
|
||||
|
||||
/**
|
||||
* Расширенный типы оплаты (9)
|
||||
* Для каждого фискального типа оплаты можно указать расширенный тип оплаты
|
||||
*/
|
||||
const ADD_9 = 9;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [
|
||||
Ffd105Tags::PAYMENT_TYPE_CASH,
|
||||
Ffd105Tags::PAYMENT_TYPE_ELECTRON,
|
||||
Ffd105Tags::PAYMENT_TYPE_PREPAID,
|
||||
Ffd105Tags::PAYMENT_TYPE_CREDIT,
|
||||
Ffd105Tags::PAYMENT_TYPE_OTHER,
|
||||
];
|
||||
}
|
||||
}
|
@ -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';
|
||||
}
|
||||
}
|
@ -1,48 +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 AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие типы налогообложения
|
||||
*
|
||||
* @package AtolOnline\Constants
|
||||
* @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35
|
||||
*/
|
||||
class SnoTypes
|
||||
final class SnoTypes extends Enum
|
||||
{
|
||||
/**
|
||||
* Общая СН
|
||||
*/
|
||||
const OSN = 'osn';
|
||||
|
||||
|
||||
/**
|
||||
* Упрощенная СН (доходы)
|
||||
*/
|
||||
const USN_INCOME = 'usn_income';
|
||||
|
||||
|
||||
/**
|
||||
* Упрощенная СН (доходы минус расходы)
|
||||
*/
|
||||
const USN_INCOME_OUTCOME = 'usn_income_outcome';
|
||||
|
||||
|
||||
/**
|
||||
* Единый налог на вмененный доход
|
||||
*/
|
||||
const ENDV = 'envd';
|
||||
|
||||
|
||||
/**
|
||||
* Единый сельскохозяйственный налог
|
||||
*/
|
||||
const ESN = 'esn';
|
||||
|
||||
|
||||
/**
|
||||
* Патентная СН
|
||||
*/
|
||||
const PATENT = 'patent';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [Ffd105Tags::COMPANY_SNO];
|
||||
}
|
||||
}
|
@ -1,58 +1,76 @@
|
||||
<?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 AtolOnline\Constants\Ffd105Tags;
|
||||
|
||||
/**
|
||||
* Константы, определяющие типы ставок НДС
|
||||
*
|
||||
* @package AtolOnline\Constants
|
||||
*/
|
||||
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%
|
||||
*/
|
||||
const VAT120 = 'vat120';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getFfdTags(): array
|
||||
{
|
||||
return [
|
||||
Ffd105Tags::ITEM_VAT_TYPE,
|
||||
Ffd105Tags::DOC_VAT_TYPE_NONE,
|
||||
Ffd105Tags::DOC_VAT_TYPE_VAT0,
|
||||
Ffd105Tags::DOC_VAT_TYPE_VAT10,
|
||||
Ffd105Tags::DOC_VAT_TYPE_VAT20,
|
||||
Ffd105Tags::DOC_VAT_TYPE_VAT110,
|
||||
Ffd105Tags::DOC_VAT_TYPE_VAT120,
|
||||
];
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user