From 77481884ad38d80afa838696134f64fccaffb36f Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 18 Nov 2021 12:24:30 +0800 Subject: [PATCH 01/85] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BF=D0=BE=20#5=20?= =?UTF-8?q?=D0=B8=20#6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - строгая типизация - переработан класс `TestEnvParams`:     - вынесен на уровень выше из под `AtolOnline\Constants`     - вместо констант - две функции для получения актуальных параметров подключения по ФФД1.05 и ФФД1.2 - актуализированы `PaymentObjects` согласно #5 - исходники вынесены не уровень выше в `src` - константы теперь enum через `myclabs/php-enum` - новые константы `DocumentTypes` - классы констант финализированы - все исключения переименованы, а многие так или иначе отрефакторены (не полностью) - новые исключения `InvalidSnoException`, `InvalidPaymentAddressException` - `helpers.php` стал полноценным классом `Helpers` - удалены трейты `HasEmail`, `HasInn`, `RublesKopeksConverter` (конвертация перенесена в `Helpers`) - удалён хелпер `valid_strlen()`, вместо него теперь везде `mb_strlen()` - сущности `Client` и `Company` получили свои имплементации для `email` и `inn` - доработки в `BasicTestCase` - полное покрытие тестами: `Client`, `Company`, `Helpers` - поправлен `phpunit.xml` - везде обновлены копирайты - актуализированы и исправлены phpdoc, return types - начато введение `strict_types=1` - минимальный php теперь 8.0 - обновлены все зависимости - подключен пакет коллекций laravel для будущего использования - теперь можно `composer test` и `composer test-cov` --- .github/workflows/dev.yml | 4 +- composer.json | 36 +- composer.lock | 1997 ++++++++++++----- phpunit.xml | 25 +- src/Api/Kkt.php | 591 +++++ src/{AtolOnline => }/Api/KktResponse.php | 25 +- src/AtolOnline/Api/Kkt.php | 576 ----- src/AtolOnline/Constants/PaymentTypes.php | 53 - src/AtolOnline/Constants/TestEnvParams.php | 49 - src/AtolOnline/Entities/Client.php | 149 -- src/AtolOnline/Entities/Company.php | 138 -- .../Exceptions/AtolEmailValidateException.php | 41 - .../AtolInnWrongLengthException.php | 44 - .../AtolWrongDocumentTypeException.php | 33 - src/AtolOnline/Traits/HasEmail.php | 55 - src/AtolOnline/Traits/HasInn.php | 54 - .../Traits/RublesKopeksConverter.php | 40 - .../Constants/Constraints.php | 17 +- .../Constants/CorrectionTypes.php | 18 +- src/Constants/DocumentTypes.php | 30 + .../Constants/PaymentMethods.php | 29 +- .../Constants/PaymentObjects.php | 97 +- src/Constants/PaymentTypes.php | 77 + .../Constants/ReceiptOperationTypes.php | 28 +- src/{AtolOnline => }/Constants/SnoTypes.php | 22 +- src/{AtolOnline => }/Constants/VatTypes.php | 26 +- src/Entities/Client.php | 213 ++ src/Entities/Company.php | 227 ++ .../Entities/CorrectionInfo.php | 28 +- src/{AtolOnline => }/Entities/Document.php | 210 +- src/{AtolOnline => }/Entities/Entity.php | 17 +- src/{AtolOnline => }/Entities/Item.php | 157 +- src/{AtolOnline => }/Entities/ItemArray.php | 34 +- src/{AtolOnline => }/Entities/Payment.php | 14 +- .../Entities/PaymentArray.php | 34 +- src/{AtolOnline => }/Entities/Vat.php | 36 +- src/{AtolOnline => }/Entities/VatArray.php | 34 +- .../Exceptions/AtolException.php | 24 +- .../AuthFailedException.php} | 26 +- .../BasicTooLongException.php} | 24 +- .../BasicTooManyException.php} | 31 +- .../EmptyCorrectionInfoException.php} | 10 +- .../EmptyEmailException.php} | 14 +- .../EmptyKktLoginException.php} | 10 +- .../EmptyKktPasswordException.php} | 10 +- .../InvalidCallbackUrlException.php} | 10 +- .../InvalidDocumentTypeException.php | 38 + src/Exceptions/InvalidEmailException.php | 41 + src/Exceptions/InvalidInnLengthException.php | 47 + .../InvalidJsonException.php} | 20 +- .../InvalidPaymentAddressException.php | 44 + src/Exceptions/InvalidSnoException.php | 45 + .../InvalidUuidException.php} | 16 +- .../TooHighPriceException.php} | 14 +- .../TooLongCallbackUrlException.php} | 10 +- .../TooLongCashierException.php} | 14 +- .../TooLongEmailException.php} | 14 +- .../TooLongKktLoginException.php} | 10 +- .../TooLongKktPasswordException.php} | 10 +- .../TooLongNameException.php} | 12 +- .../TooLongPaymentAddressException.php} | 14 +- .../TooLongPhoneException.php} | 12 +- .../TooLongUnitException.php} | 14 +- .../TooLongUserdataException.php} | 14 +- .../TooManyItemsException.php} | 10 +- .../TooManyPaymentsException.php} | 12 +- .../TooManyVatsException.php} | 12 +- src/Helpers.php | 55 + src/TestEnvParams.php | 58 + src/helpers.php | 16 - tests/BasicTestCase.php | 82 +- tests/ClientTest.php | 317 +++ tests/CompanyTest.php | 134 ++ tests/HelpersTest.php | 123 + .../ItemTest.php => ItemTest_todo.php} | 85 +- tests/Unit/ClientTest.php | 103 - tests/Unit/CompanyTest.php | 91 - tests/{Unit/VatTest.php => VatTest_todo.php} | 28 +- 78 files changed, 4220 insertions(+), 2712 deletions(-) create mode 100644 src/Api/Kkt.php rename src/{AtolOnline => }/Api/KktResponse.php (85%) delete mode 100644 src/AtolOnline/Api/Kkt.php delete mode 100644 src/AtolOnline/Constants/PaymentTypes.php delete mode 100644 src/AtolOnline/Constants/TestEnvParams.php delete mode 100644 src/AtolOnline/Entities/Client.php delete mode 100644 src/AtolOnline/Entities/Company.php delete mode 100644 src/AtolOnline/Exceptions/AtolEmailValidateException.php delete mode 100644 src/AtolOnline/Exceptions/AtolInnWrongLengthException.php delete mode 100644 src/AtolOnline/Exceptions/AtolWrongDocumentTypeException.php delete mode 100644 src/AtolOnline/Traits/HasEmail.php delete mode 100644 src/AtolOnline/Traits/HasInn.php delete mode 100644 src/AtolOnline/Traits/RublesKopeksConverter.php rename src/{AtolOnline => }/Constants/Constraints.php (84%) rename src/{AtolOnline => }/Constants/CorrectionTypes.php (61%) create mode 100644 src/Constants/DocumentTypes.php rename src/{AtolOnline => }/Constants/PaymentMethods.php (79%) rename src/{AtolOnline => }/Constants/PaymentObjects.php (57%) create mode 100644 src/Constants/PaymentTypes.php rename src/{AtolOnline => }/Constants/ReceiptOperationTypes.php (68%) rename src/{AtolOnline => }/Constants/SnoTypes.php (83%) rename src/{AtolOnline => }/Constants/VatTypes.php (78%) create mode 100644 src/Entities/Client.php create mode 100644 src/Entities/Company.php rename src/{AtolOnline => }/Entities/CorrectionInfo.php (87%) rename src/{AtolOnline => }/Entities/Document.php (68%) rename src/{AtolOnline => }/Entities/Entity.php (55%) rename src/{AtolOnline => }/Entities/Item.php (68%) rename src/{AtolOnline => }/Entities/ItemArray.php (78%) rename src/{AtolOnline => }/Entities/Payment.php (88%) rename src/{AtolOnline => }/Entities/PaymentArray.php (77%) rename src/{AtolOnline => }/Entities/Vat.php (87%) rename src/{AtolOnline => }/Entities/VatArray.php (78%) rename src/{AtolOnline => }/Exceptions/AtolException.php (75%) rename src/{AtolOnline/Exceptions/AtolAuthFailedException.php => Exceptions/AuthFailedException.php} (55%) rename src/{AtolOnline/Exceptions/AtolTooLongException.php => Exceptions/BasicTooLongException.php} (55%) rename src/{AtolOnline/Exceptions/AtolTooManyException.php => Exceptions/BasicTooManyException.php} (50%) rename src/{AtolOnline/Exceptions/AtolCorrectionInfoException.php => Exceptions/EmptyCorrectionInfoException.php} (76%) rename src/{AtolOnline/Exceptions/AtolEmailEmptyException.php => Exceptions/EmptyEmailException.php} (73%) rename src/{AtolOnline/Exceptions/AtolKktLoginEmptyException.php => Exceptions/EmptyKktLoginException.php} (75%) rename src/{AtolOnline/Exceptions/AtolKktPasswordEmptyException.php => Exceptions/EmptyKktPasswordException.php} (74%) rename src/{AtolOnline/Exceptions/AtolInvalidCallbackUrlException.php => Exceptions/InvalidCallbackUrlException.php} (74%) create mode 100644 src/Exceptions/InvalidDocumentTypeException.php create mode 100644 src/Exceptions/InvalidEmailException.php create mode 100644 src/Exceptions/InvalidInnLengthException.php rename src/{AtolOnline/Exceptions/AtolInvalidJsonException.php => Exceptions/InvalidJsonException.php} (59%) create mode 100644 src/Exceptions/InvalidPaymentAddressException.php create mode 100644 src/Exceptions/InvalidSnoException.php rename src/{AtolOnline/Exceptions/AtolInvalidUuidException.php => Exceptions/InvalidUuidException.php} (70%) rename src/{AtolOnline/Exceptions/AtolPriceTooHighException.php => Exceptions/TooHighPriceException.php} (73%) rename src/{AtolOnline/Exceptions/AtolCallbackUrlTooLongException.php => Exceptions/TooLongCallbackUrlException.php} (74%) rename src/{AtolOnline/Exceptions/AtolCashierTooLongException.php => Exceptions/TooLongCashierException.php} (73%) rename src/{AtolOnline/Exceptions/AtolEmailTooLongException.php => Exceptions/TooLongEmailException.php} (73%) rename src/{AtolOnline/Exceptions/AtolKktLoginTooLongException.php => Exceptions/TooLongKktLoginException.php} (74%) rename src/{AtolOnline/Exceptions/AtolKktPasswordTooLongException.php => Exceptions/TooLongKktPasswordException.php} (74%) rename src/{AtolOnline/Exceptions/AtolNameTooLongException.php => Exceptions/TooLongNameException.php} (75%) rename src/{AtolOnline/Exceptions/AtolPaymentAddressTooLongException.php => Exceptions/TooLongPaymentAddressException.php} (73%) rename src/{AtolOnline/Exceptions/AtolPhoneTooLongException.php => Exceptions/TooLongPhoneException.php} (75%) rename src/{AtolOnline/Exceptions/AtolUnitTooLongException.php => Exceptions/TooLongUnitException.php} (73%) rename src/{AtolOnline/Exceptions/AtolUserdataTooLongException.php => Exceptions/TooLongUserdataException.php} (74%) rename src/{AtolOnline/Exceptions/AtolTooManyItemsException.php => Exceptions/TooManyItemsException.php} (75%) rename src/{AtolOnline/Exceptions/AtolTooManyPaymentsException.php => Exceptions/TooManyPaymentsException.php} (75%) rename src/{AtolOnline/Exceptions/AtolTooManyVatsException.php => Exceptions/TooManyVatsException.php} (76%) create mode 100644 src/Helpers.php create mode 100644 src/TestEnvParams.php delete mode 100644 src/helpers.php create mode 100644 tests/ClientTest.php create mode 100644 tests/CompanyTest.php create mode 100644 tests/HelpersTest.php rename tests/{Feature/ItemTest.php => ItemTest_todo.php} (68%) delete mode 100644 tests/Unit/ClientTest.php delete mode 100644 tests/Unit/CompanyTest.php rename tests/{Unit/VatTest.php => VatTest_todo.php} (61%) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 5bdc0de..7c70f41 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -16,10 +16,10 @@ jobs: uses: php-actions/composer@40-env with: version: 2 - php_version: 7.4 + php_version: 8.0 only_args: --prefer-dist --no-progress - name: Run phpunit tests - uses: php-actions/phpunit@v8 + uses: php-actions/phpunit@v9 with: configuration: ./phpunit.xml diff --git a/composer.json b/composer.json index 352104b..c9438f6 100644 --- a/composer.json +++ b/composer.json @@ -39,26 +39,30 @@ } ], "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" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.5" }, "autoload": { - "classmap": [ - "src/AtolOnline/Api/", - "src/AtolOnline/Exceptions/", - "src/AtolOnline/Entities/", - "src/AtolOnline/Traits/", - "src/AtolOnline/Constants/", - "tests/" - ], - "files": [ - "src/helpers.php" - ] + "psr-4": { + "AtolOnline\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "AtolOnline\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "vendor/bin/phpunit --colors=always", + "test-cov": "vendor/bin/phpunit --coverage-html coverage" } } diff --git a/composer.lock b/composer.lock index 47340ed..8acd671 100644 --- a/composer.lock +++ b/composer.lock @@ -4,41 +4,105 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5e1b55b0c481ba8d05be29bf5310f5ef", + "content-hash": "b03e06fba2a861f7ce366484ec6421c3", "packages": [ { - "name": "guzzlehttp/guzzle", - "version": "6.5.5", + "name": "brick/math", + "version": "0.9.3", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" + "url": "https://github.com/brick/math.git", + "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", - "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", + "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", + "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.6.1", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.17.0" + "php": "^7.1 || ^8.0" }, "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", + "vimeo/psalm": "4.9.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/brick/math", + "type": "tidelift" + } + ], + "time": "2021-08-15T20:50:18+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.4.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94", + "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.1" + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5-dev" + "dev-master": "7.4-dev" } }, "autoload": { @@ -54,41 +118,82 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", + "psr-18", + "psr-7", "rest", "web service" ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/6.5" - }, - "time": "2020-06-16T21:01:06+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2021-10-18T09:52:00+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "" }, "require": { @@ -100,7 +205,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -116,47 +221,75 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.1" - }, - "time": "2021-03-07T09:25:29+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.8.2", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" + "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72", + "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -164,30 +297,53 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "2.1-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", @@ -201,61 +357,358 @@ "uri", "url" ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.2" - }, - "time": "2021-04-26T09:17:50+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-06T17:43:30+00:00" }, { - "name": "paragonie/random_compat", - "version": "v9.99.99", + "name": "illuminate/collections", + "version": "v8.70.2", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "url": "https://github.com/illuminate/collections.git", + "reference": "05f286ec5fd2dd286e8384577047efc375c8954c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/illuminate/collections/zipball/05f286ec5fd2dd286e8384577047efc375c8954c", + "reference": "05f286ec5fd2dd286e8384577047efc375c8954c", "shasum": "" }, "require": { - "php": "^7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "php": "^7.3|^8.0" }, "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + "symfony/var-dumper": "Required to use the dump method (^5.1.4)." }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "helpers.php" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "time": "2021-10-22T18:01:46+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v8.70.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "e76f4bce73a2a1656add24bd5210ebc4b8af49c0" }, - "time": "2018-07-02T15:55:56+00:00" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/e76f4bce73a2a1656add24bd5210ebc4b8af49c0", + "reference": "e76f4bce73a2a1656add24bd5210ebc4b8af49c0", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0", + "psr/container": "^1.0", + "psr/simple-cache": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "time": "2021-10-22T18:01:46+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v8.70.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "300aa13c086f25116b5f3cde3ca54ff5c822fb05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/300aa13c086f25116b5f3cde3ca54ff5c822fb05", + "reference": "300aa13c086f25116b5f3cde3ca54ff5c822fb05", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "time": "2020-10-27T15:20:30+00:00" + }, + { + "name": "myclabs/php-enum", + "version": "1.8.3", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "b942d263c641ddb5190929ff840c68f78713e937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937", + "reference": "b942d263c641ddb5190929ff840c68f78713e937", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "1.*", + "vimeo/psalm": "^4.6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ], + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", + "type": "tidelift" + } + ], + "time": "2021-07-05T08:18:36+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", @@ -312,30 +765,30 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -355,10 +808,55 @@ "psr", "psr-3" ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" }, - "time": "2021-05-03T11:20:27+00:00" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-10-23T01:57:42+00:00" }, { "name": "ralouphie/getallheaders", @@ -405,54 +903,142 @@ "time": "2019-03-08T08:55:37+00:00" }, { - "name": "ramsey/uuid", - "version": "3.9.3", + "name": "ramsey/collection", + "version": "1.2.2", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "7e1633a6964b48589b142d60542f9ed31bd37a92" + "url": "https://github.com/ramsey/collection.git", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/7e1633a6964b48589b142d60542f9ed31bd37a92", - "reference": "7e1633a6964b48589b142d60542f9ed31bd37a92", + "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a", + "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a", "shasum": "" }, "require": { + "php": "^7.3 || ^8", + "symfony/polyfill-php81": "^1.23" + }, + "require-dev": { + "captainhook/captainhook": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "ergebnis/composer-normalize": "^2.6", + "fakerphp/faker": "^1.5", + "hamcrest/hamcrest-php": "^2", + "jangregor/phpstan-prophecy": "^0.8", + "mockery/mockery": "^1.3", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^0.12.32", + "phpstan/phpstan-mockery": "^0.12.5", + "phpstan/phpstan-phpunit": "^0.12.11", + "phpunit/phpunit": "^8.5 || ^9", + "psy/psysh": "^0.10.4", + "slevomat/coding-standard": "^6.3", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2021-10-10T03:01:02+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", + "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", + "shasum": "" + }, + "require": { + "brick/math": "^0.8 || ^0.9", "ext-json": "*", - "paragonie/random_compat": "^1 | ^2 | 9.99.99", - "php": "^5.4 | ^7 | ^8", - "symfony/polyfill-ctype": "^1.8" + "php": "^7.2 || ^8.0", + "ramsey/collection": "^1.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-php80": "^1.14" }, "replace": { "rhumsaa/uuid": "self.version" }, "require-dev": { - "codeception/aspect-mock": "^1 | ^2", - "doctrine/annotations": "^1.2", - "goaop/framework": "1.0.0-alpha.2 | ^1 | ^2.1", - "jakub-onderka/php-parallel-lint": "^1", - "mockery/mockery": "^0.9.11 | ^1", + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", "moontoast/math": "^1.1", "paragonie/random-lib": "^2", - "php-mock/php-mock-phpunit": "^0.3 | ^1.1", - "phpunit/phpunit": "^4.8 | ^5.4 | ^6.5", - "squizlabs/php_codesniffer": "^3.5" + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-mockery": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^8.5 || ^9", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" }, "suggest": { - "ext-ctype": "Provides support for PHP Ctype functions", - "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", - "ext-openssl": "Provides the OpenSSL extension for use with the OpenSslGenerator", - "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", - "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-ctype": "Enables faster processing of character classification using ctype functions.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-main": "4.x-dev" + }, + "captainhook": { + "force-install": true } }, "autoload": { @@ -467,48 +1053,100 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - }, - { - "name": "Marijn Huizendveld", - "email": "marijn.huizendveld@gmail.com" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io" - } - ], - "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", - "homepage": "https://github.com/ramsey/uuid", + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", "keywords": [ "guid", "identifier", "uuid" ], - "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "rss": "https://github.com/ramsey/uuid/releases.atom", - "source": "https://github.com/ramsey/uuid", - "wiki": "https://github.com/ramsey/uuid/wiki" - }, - "time": "2020-02-21T04:36:14+00:00" + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2021-09-25T23:10:38+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.22.1", + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", - "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-23T23:28:01+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", "shasum": "" }, "require": { @@ -520,7 +1158,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -557,9 +1195,6 @@ "polyfill", "portable" ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -574,34 +1209,29 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { - "name": "symfony/polyfill-intl-idn", - "version": "v1.22.1", + "name": "symfony/polyfill-php80", + "version": "v1.23.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", - "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -610,10 +1240,13 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "files": [ "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -622,31 +1255,26 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" }, { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "idn", - "intl", "polyfill", "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -661,32 +1289,29 @@ "type": "tidelift" } ], - "time": "2021-01-22T09:19:47+00:00" + "time": "2021-07-28T13:41:28+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.22.1", + "name": "symfony/polyfill-php81", + "version": "v1.23.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "e66119f3de95efc359483f810c4c3e6436279436" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", - "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436", + "reference": "e66119f3de95efc359483f810c4c3e6436279436", "shasum": "" }, "require": { "php": ">=7.1" }, - "suggest": { - "ext-intl": "For best performance" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -695,7 +1320,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "files": [ "bootstrap.php" @@ -718,19 +1343,14 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "intl", - "normalizer", "polyfill", "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -745,83 +1365,7 @@ "type": "tidelift" } ], - "time": "2021-01-22T09:19:47+00:00" - }, - { - "name": "symfony/polyfill-php72", - "version": "v1.22.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", - "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.22-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-05-21T13:25:03+00:00" } ], "packages-dev": [ @@ -953,17 +1497,69 @@ "time": "2020-11-13T09:40:50+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.1", + "name": "nikic/php-parser", + "version": "v4.13.1", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2021-11-03T20:52:16+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { @@ -1006,11 +1602,7 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" - }, - "time": "2020-06-27T14:33:11+00:00" + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", @@ -1118,16 +1710,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { @@ -1138,7 +1730,8 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -1166,24 +1759,20 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" - }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { @@ -1191,7 +1780,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -1215,41 +1805,37 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" - }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", + "phpspec/phpspec": "^6.0 || ^7.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -1282,48 +1868,48 @@ "spy", "stub" ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" - }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.14", + "version": "9.2.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c" + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c", - "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": ">=7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1 || ^4.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^8.2.2" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.7.2" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -1349,42 +1935,38 @@ "testing", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-12-02T13:39:03+00:00" + "time": "2021-10-30T08:01:38+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357" + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357", - "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1409,36 +1991,99 @@ "filesystem", "iterator" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T08:25:21+00:00" + "time": "2020-09-28T05:57:25+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -1460,36 +2105,38 @@ "keywords": [ "template" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" }, { "name": "phpunit/php-timer", - "version": "2.1.3", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1513,90 +2160,26 @@ "keywords": [ "timer" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T08:20:02+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", - "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2020-08-04T08:28:15+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.15", + "version": "9.5.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "038d4196d8e8cb405cd5e82cedfe413ad6eef9ef" + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/038d4196d8e8cb405cd5e82cedfe413ad6eef9ef", - "reference": "038d4196d8e8cb405cd5e82cedfe413ad6eef9ef", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", "shasum": "" }, "require": { @@ -1607,32 +2190,35 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.0", - "phar-io/manifest": "^2.0.1", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.2", - "phpspec/prophecy": "^1.10.3", - "phpunit/php-code-coverage": "^7.0.12", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.3", - "sebastian/exporter": "^3.1.2", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-pdo": "*" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-xdebug": "*" }, "bin": [ "phpunit" @@ -1640,12 +2226,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.5-dev" + "dev-master": "9.5-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1666,10 +2255,6 @@ "testing", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.15" - }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -1680,32 +2265,136 @@ "type": "github" } ], - "time": "2021-03-17T07:27:54+00:00" + "time": "2021-09-25T07:38:51+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", + "name": "sebastian/cli-parser", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" } }, "autoload": { @@ -1725,44 +2414,40 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T08:15:22+00:00" + "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.3", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1799,43 +2484,92 @@ "compare", "equality" ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2020-10-26T15:49:45+00:00" }, { - "name": "sebastian/diff", - "version": "3.0.3", + "name": "sebastian/complexity", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { - "php": ">=7.1" + "nikic/php-parser": "^4.7", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { @@ -1865,37 +2599,33 @@ "unidiff", "unified diff" ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", - "version": "4.2.4", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" @@ -1903,7 +2633,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -1928,44 +2658,40 @@ "environment", "hhvm" ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:53:42+00:00" + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2000,45 +2726,41 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:47:53+00:00" + "time": "2021-11-11T14:18:36+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { - "php": ">=7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -2046,7 +2768,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2069,44 +2791,93 @@ "keywords": [ "global state" ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:43:24+00:00" + "time": "2021-06-11T13:31:12+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "3.0.4", + "name": "sebastian/lines-of-code", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "nikic/php-parser": "^4.6", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { @@ -2126,42 +2897,38 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:40:27+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2181,42 +2948,38 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:37:18+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2244,39 +3007,38 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:34:24+00:00" + "time": "2020-10-26T13:17:30+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2296,42 +3058,38 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:30:19+00:00" + "time": "2020-09-28T06:45:17+00:00" }, { "name": "sebastian/type", - "version": "1.1.4", + "version": "2.3.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -2352,39 +3110,35 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:25:11+00:00" + "time": "2021-06-15T12:49:02+00:00" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2405,24 +3159,26 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, - "time": "2016-10-03T07:35:21+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "75a63c33a8577608444246075ea0af0d052e452a" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", - "reference": "75a63c33a8577608444246075ea0af0d052e452a", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { @@ -2449,17 +3205,13 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/master" - }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-07-12T23:59:07+00:00" + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", @@ -2526,9 +3278,10 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4", - "ext-json": "*" + "php": ">=8.0", + "ext-json": "*", + "ext-mbstring": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "1.1.0" } diff --git a/phpunit.xml b/phpunit.xml index 8814837..45ebb1c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,4 +1,12 @@ + + - - ClientTest.php - CompanyTest.php - VatTest.php - ./tests/Unit - - - ItemTest.php - ./tests/Feature + + ./tests + + + + src + + \ No newline at end of file diff --git a/src/Api/Kkt.php b/src/Api/Kkt.php new file mode 100644 index 0000000..7459c74 --- /dev/null +++ b/src/Api/Kkt.php @@ -0,0 +1,591 @@ +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): Kkt + { + $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 EmptyKktLoginException Логин ККТ не может быть пустым + * @throws TooLongKktLoginException Слишком длинный логин ККТ + */ + public function setLogin(string $login): Kkt + { + if (empty($login)) { + throw new EmptyKktLoginException(); + } elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) { + throw new TooLongKktLoginException($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 EmptyKktPasswordException Пароль ККТ не может быть пустым + * @throws TooLongKktPasswordException Слишком длинный пароль ККТ + */ + public function setPassword(string $password): Kkt + { + if (empty($password)) { + throw new EmptyKktPasswordException(); + } elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) { + throw new TooLongKktPasswordException($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 TooLongCallbackUrlException Слишком длинный Callback URL + * @throws InvalidCallbackUrlException Невалидный Callback URL + */ + public function setCallbackUrl(string $url): Kkt + { + if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) { + throw new TooLongCallbackUrlException($url, Constraints::MAX_LENGTH_CALLBACK_URL); + } elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) { + throw new InvalidCallbackUrlException('Callback URL not matches with pattern'); + } + $this->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 KktResponse|null + */ + public function getLastResponse(): ?KktResponse + { + 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): Kkt + { + $this->is_test_mode = $test_mode; + return $this; + } + + /** + * Регистрирует документ прихода + * + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе есть данные коррекции + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function sell(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Некорректная операция над документом коррекции'); + } + return $this->registerDocument('sell', 'receipt', $document, $external_id); + } + + /** + * Регистрирует документ возврата прихода + * + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе есть данные коррекции + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws TooManyVatsException Слишком много ставок НДС + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function sellRefund(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Invalid operation on correction document'); + } + return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id); + } + + /** + * Регистрирует документ коррекции прихода + * + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws TooManyItemsException Слишком много предметов расчёта + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function sellCorrection(Document $document, ?string $external_id = null): KktResponse + { + if (!$document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException(); + } + $document->setClient(null)->setItems([]); + return $this->registerDocument('sell_correction', 'correction', $document, $external_id); + } + + /** + * Регистрирует документ расхода + * + * @param Document $document + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе есть данные коррекции + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function buy(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Invalid operation on correction document'); + } + return $this->registerDocument('buy', 'receipt', $document, $external_id); + } + + /** + * Регистрирует документ возврата расхода + * + * @param Document $document + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе есть данные коррекции + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws TooManyVatsException Слишком много ставок НДС + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function buyRefund(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Invalid operation on correction document'); + } + return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id); + } + + /** + * Регистрирует документ коррекции расхода + * + * @param Document $document + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции + * @throws InvalidInnLengthException Некорректная длтина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws TooManyItemsException Слишком много предметов расчёта + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function buyCorrection(Document $document, ?string $external_id = null): KktResponse + { + if (!$document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException(); + } + $document->setClient(null)->setItems([]); + return $this->registerDocument('buy_correction', 'correction', $document, $external_id); + } + + /** + * Проверяет статус чека на ККТ один раз + * + * @param string $uuid UUID регистрации + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws InvalidUuidException Некорректный UUID документа + * @throws GuzzleException + */ + public function getDocumentStatus(string $uuid): KktResponse + { + $uuid = trim($uuid); + if (!Uuid::isValid($uuid)) { + throw new InvalidUuidException($uuid); + } + $this->auth(); + return $this->sendAtolRequest('GET', 'report/' . $uuid); + } + + /** + * Проверяет статус чека на ККТ нужное количество раз с указанным интервалом. + * Вернёт результат как только при очередной проверке сменится статус регистрации документа. + * + * @param string $uuid UUID регистрации + * @param int $retry_count Количество попыток + * @param int $timeout Таймаут в секундах между попытками + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws InvalidUuidException Некорректный UUID документа + * @throws GuzzleException + */ + public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): KktResponse + { + $try = 0; + do { + $response = $this->getDocumentStatus($uuid); + if ($response->isValid() && $response->getContent()->status == 'done') { + break; + } else { + sleep($timeout); + } + ++$try; + } while ($try < $retry_count); + return $response; + } + + /** + * Возвращает текущий токен авторизации + * + * @return string + */ + public function getAuthToken(): ?string + { + return $this->auth_token; + } + + /** + * Устанавливает заранее известный токен авторизации + * + * @param string|null $auth_token + * @return $this + */ + public function setAuthToken(?string $auth_token): Kkt + { + $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::FFD105()['group']; + $this->kkt_config['test']['login'] = TestEnvParams::FFD105()['login']; + $this->kkt_config['test']['pass'] = TestEnvParams::FFD105()['password']; + $this->kkt_config['test']['url'] = 'https://testonline.atol.ru/possystem/v4'; + $this->kkt_config['test']['callback_url'] = ''; + } + + /** + * Возвращает набор заголовков для HTTP-запроса + * + * @return array + */ + protected function getHeaders(): array + { + $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): string + { + $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 KktResponse + * @throws 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 + ): KktResponse { + $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 AuthFailedException Ошибка авторизации + * @throws GuzzleException + */ + protected function auth(): bool + { + if (!$this->getAuthToken()) { + $result = $this->sendAtolRequest('GET', 'getToken', [ + 'login' => $this->getLogin(), + 'pass' => $this->getPassword(), + ]); + if (!$result->isValid() || !$result->getContent()->token) { + throw new AuthFailedException($result); + } + $this->auth_token = $result->getContent()->token; + } + return true; + } + + /** + * Отправляет документ на регистрацию + * + * @param string $api_method Метод API + * @param string $type Тип документа: receipt, correction + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws GuzzleException + * @throws Exception + */ + protected function registerDocument( + string $api_method, + string $type, + Document $document, + ?string $external_id = null + ): KktResponse { + $type = trim($type); + if (!in_array($type, ['receipt', 'correction'])) { + throw new InvalidDocumentTypeException($type); + } + $this->auth(); + if ($this->isTestMode()) { + $document->setCompany(($document->getCompany() ?: new Company()) + ->setInn(TestEnvParams::FFD105()['inn']) + ->setSno(TestEnvParams::FFD105()['sno']) + ->setPaymentAddress(TestEnvParams::FFD105()['payment_address'])); + } + $data['timestamp'] = date('d.m.y H:i:s'); + $data['external_id'] = $external_id ?: Uuid::uuid4()->toString(); + $data[$type] = $document; + if ($this->getCallbackUrl()) { + $data['service'] = ['callback_url' => $this->getCallbackUrl()]; + } + return $this->sendAtolRequest('POST', trim($api_method), $data); + } +} diff --git a/src/AtolOnline/Api/KktResponse.php b/src/Api/KktResponse.php similarity index 85% rename from src/AtolOnline/Api/KktResponse.php rename to src/Api/KktResponse.php index c52bb9b..7fe359a 100644 --- a/src/AtolOnline/Api/KktResponse.php +++ b/src/Api/KktResponse.php @@ -1,12 +1,14 @@ code = $response->getStatusCode(); $this->headers = $response->getHeaders(); - $this->content = json_decode($response->getBody()); + $this->content = json_decode((string)$response->getBody()); } /** @@ -93,12 +96,12 @@ class KktResponse implements JsonSerializable * * @return bool */ - public function isValid() + public function isValid(): bool { return !empty($this->getCode()) && !empty($this->getContent()) && empty($this->getContent()->error) - && (int)$this->getCode() < 400; + && $this->getCode() < 400; } /** diff --git a/src/AtolOnline/Api/Kkt.php b/src/AtolOnline/Api/Kkt.php deleted file mode 100644 index 54c2d45..0000000 --- a/src/AtolOnline/Api/Kkt.php +++ /dev/null @@ -1,576 +0,0 @@ -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); - } -} diff --git a/src/AtolOnline/Constants/PaymentTypes.php b/src/AtolOnline/Constants/PaymentTypes.php deleted file mode 100644 index 80202f7..0000000 --- a/src/AtolOnline/Constants/PaymentTypes.php +++ /dev/null @@ -1,53 +0,0 @@ -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; - } -} diff --git a/src/AtolOnline/Entities/Company.php b/src/AtolOnline/Entities/Company.php deleted file mode 100644 index 97b3e69..0000000 --- a/src/AtolOnline/Entities/Company.php +++ /dev/null @@ -1,138 +0,0 @@ -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(), - ]; - } -} \ No newline at end of file diff --git a/src/AtolOnline/Exceptions/AtolEmailValidateException.php b/src/AtolOnline/Exceptions/AtolEmailValidateException.php deleted file mode 100644 index ff1bcdb..0000000 --- a/src/AtolOnline/Exceptions/AtolEmailValidateException.php +++ /dev/null @@ -1,41 +0,0 @@ -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; - } -} \ No newline at end of file diff --git a/src/AtolOnline/Traits/HasInn.php b/src/AtolOnline/Traits/HasInn.php deleted file mode 100644 index 856055a..0000000 --- a/src/AtolOnline/Traits/HasInn.php +++ /dev/null @@ -1,54 +0,0 @@ -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; - } -} \ No newline at end of file diff --git a/src/AtolOnline/Traits/RublesKopeksConverter.php b/src/AtolOnline/Traits/RublesKopeksConverter.php deleted file mode 100644 index 95ecc78..0000000 --- a/src/AtolOnline/Traits/RublesKopeksConverter.php +++ /dev/null @@ -1,40 +0,0 @@ - копейки - * - * @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); - } -} diff --git a/src/AtolOnline/Constants/Constraints.php b/src/Constants/Constraints.php similarity index 84% rename from src/AtolOnline/Constants/Constraints.php rename to src/Constants/Constraints.php index 92c1040..5ca70b7 100644 --- a/src/AtolOnline/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -1,19 +1,26 @@ setName($name); + $email && $this->setEmail($email); + $phone && $this->setPhone($phone); + $inn && $this->setInn($inn); + } + + /** + * Возвращает наименование покупателя + * + * Тег ФФД - 1227 + * + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * Устанавливает наименование покупателя + * + * Тег ФФД - 1227 + * + * @param string|null $name + * @return $this + * @throws TooLongNameException + */ + public function setName(?string $name): Client + { + if (is_string($name)) { + $name = preg_replace('/[\n\r\t]/', '', trim($name)); + if (mb_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) { + throw new TooLongNameException($name, Constraints::MAX_LENGTH_CLIENT_NAME); + } + } + $this->name = empty($name) ? null : $name; + return $this; + } + + /** + * Возвращает установленный email + * + * @return string|null + */ + public function getEmail(): ?string + { + return $this->email; + } + + /** + * Устанавливает email + * + * @param string|null $email + * @return $this + * @throws TooLongEmailException Слишком длинный email + * @throws InvalidEmailException Невалидный email + */ + public function setEmail(?string $email): self + { + if (is_string($email)) { + $email = preg_replace('/[\n\r\t]/', '', trim($email)); + if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) { + throw new TooLongEmailException($email, Constraints::MAX_LENGTH_EMAIL); + } elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { + throw new InvalidEmailException($email); + } + } + $this->email = empty($email) ? null : $email; + return $this; + } + + /** + * Возвращает установленный телефон + * + * Тег ФФД - 1008 + * + * @return string|null + */ + public function getPhone(): ?string + { + return $this->phone; + } + + /** + * Устанавливает телефон + * + * Тег ФФД - 1008 + * + * @param string|null $phone Номер телефона + * @return $this + * @throws TooLongPhoneException + */ + public function setPhone(?string $phone): Client + { + if (is_string($phone)) { + $phone = preg_replace('/[^\d]/', '', trim($phone)); + if (mb_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_PHONE) { + throw new TooLongPhoneException($phone, Constraints::MAX_LENGTH_CLIENT_PHONE); + } + } + $this->phone = empty($phone) ? null : "+$phone"; + return $this; + } + + /** + * Возвращает установленный ИНН + * + * @return string|null + */ + public function getInn(): ?string + { + return $this->inn; + } + + /** + * Устанавливает ИНН + * + * @param string|null $inn + * @return $this + * @throws InvalidInnLengthException Некорректная длина ИНН + */ + public function setInn(?string $inn): self + { + if (is_string($inn)) { + $inn = preg_replace('/[^\d]/', '', trim($inn)); + if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { + throw new InvalidInnLengthException($inn); + } + } + $this->inn = empty($inn) ? null : $inn; + return $this; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): object + { + $json = []; + $this->getName() && $json['name'] = $this->getName(); + $this->getEmail() && $json['email'] = $this->getEmail(); + $this->getPhone() && $json['phone'] = $this->getPhone(); + $this->getInn() && $json['inn'] = $this->getInn(); + return (object)$json; + } +} diff --git a/src/Entities/Company.php b/src/Entities/Company.php new file mode 100644 index 0000000..f37f955 --- /dev/null +++ b/src/Entities/Company.php @@ -0,0 +1,227 @@ +setEmail($email); + $this->setSno($sno); + $this->setInn($inn); + $this->setPaymentAddress($payment_address); + } + + /** + * Возвращает установленный email + * + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * Устанавливает email + * + * @param string $email + * @return $this + * @throws TooLongEmailException Слишком длинный email + * @throws InvalidEmailException Невалидный email + */ + public function setEmail(string $email): self + { + $email = preg_replace('/[\n\r\t]/', '', trim($email)); + if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) { + throw new TooLongEmailException($email, Constraints::MAX_LENGTH_EMAIL); + } elseif (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) { + throw new InvalidEmailException($email); + } + $this->email = $email; + return $this; + } + + /** + * Возвращает установленный тип налогообложения + * + * Тег ФФД - 1055 + * + * @return string + */ + public function getSno(): string + { + return $this->sno; + } + + /** + * Устанавливает тип налогообложения + * + * Тег ФФД - 1055 + * + * @param string $sno + * @return $this + * @throws InvalidSnoException + */ + public function setSno(string $sno): Company + { + $sno = trim($sno); + if (empty($sno) || !in_array($sno, SnoTypes::toArray())) { + throw new InvalidSnoException($sno); + } + $this->sno = $sno; + return $this; + } + + /** + * Возвращает установленный ИНН + * + * Тег ФФД - 1018 + * + * @return string + */ + public function getInn(): string + { + return $this->inn; + } + + /** + * Устанавливает ИНН + * + * Тег ФФД - 1018 + * + * @param string $inn + * @return $this + * @throws InvalidInnLengthException + */ + public function setInn(string $inn): self + { + $inn = preg_replace('/[^\d]/', '', trim($inn)); + if (empty($inn) || preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { + throw new InvalidInnLengthException($inn); + } + $this->inn = $inn; + return $this; + } + + /** + * Возвращает установленный адрес места расчётов + * + * Тег ФФД - 1187 + * + * @return string + */ + public function getPaymentAddress(): string + { + return $this->payment_address; + } + + /** + * Устанавливает адрес места расчётов + * + * Тег ФФД - 1187 + * + * @param string $payment_address + * @return $this + * @throws TooLongPaymentAddressException + * @throws InvalidPaymentAddressException + */ + public function setPaymentAddress(string $payment_address): Company + { + $payment_address = trim($payment_address); + if (empty($payment_address)) { + throw new InvalidPaymentAddressException(); + } elseif (mb_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) { + throw new TooLongPaymentAddressException($payment_address, Constraints::MAX_LENGTH_PAYMENT_ADDRESS); + } + $this->payment_address = $payment_address; + return $this; + } + + /** + * @inheritDoc + * @throws InvalidEmailException + * @throws InvalidSnoException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + */ + public function jsonSerialize(): object + { + return (object)[ + 'email' => $this->email + ? $this->getEmail() + : throw new InvalidEmailException(), + 'sno' => $this->sno + ? $this->getSno() + : throw new InvalidSnoException(), + 'inn' => $this->inn + ? $this->getInn() + : throw new InvalidInnLengthException(), + 'payment_address' => $this->payment_address + ? $this->getPaymentAddress() + : throw new InvalidPaymentAddressException(), + ]; + } +} \ No newline at end of file diff --git a/src/AtolOnline/Entities/CorrectionInfo.php b/src/Entities/CorrectionInfo.php similarity index 87% rename from src/AtolOnline/Entities/CorrectionInfo.php rename to src/Entities/CorrectionInfo.php index 2cbde55..d6f11e6 100644 --- a/src/AtolOnline/Entities/CorrectionInfo.php +++ b/src/Entities/CorrectionInfo.php @@ -1,40 +1,40 @@ base_number = trim($number); return $this; @@ -102,7 +102,7 @@ class CorrectionInfo extends Entity * @param string $name * @return $this */ - public function setName(string $name) + public function setName(string $name): CorrectionInfo { $this->base_name = trim($name); return $this; @@ -126,7 +126,7 @@ class CorrectionInfo extends Entity * @param string $date Строка в формате d.m.Y * @return $this */ - public function setDate(string $date) + public function setDate(string $date): CorrectionInfo { $this->base_date = $date; return $this; @@ -150,7 +150,7 @@ class CorrectionInfo extends Entity * @param string $type * @return $this */ - public function setType(string $type) + public function setType(string $type): CorrectionInfo { $this->type = $type; return $this; diff --git a/src/AtolOnline/Entities/Document.php b/src/Entities/Document.php similarity index 68% rename from src/AtolOnline/Entities/Document.php rename to src/Entities/Document.php index 66716fd..af72077 100644 --- a/src/AtolOnline/Entities/Document.php +++ b/src/Entities/Document.php @@ -1,18 +1,34 @@ payments = new PaymentArray(); $this->items = new ItemArray(); } - + /** * Удаляет все налоги из документа и предметов расчёта * * @return $this - * @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма - * @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС + * @throws TooManyVatsException Слишком много ставок НДС */ - public function clearVats() + public function clearVats(): Document { $this->setVats([]); return $this; } - + /** * Добавляет новую ставку НДС в массив ставок НДС * - * @param \AtolOnline\Entities\Vat $vat Объект ставки НДС + * @param Vat $vat Объект ставки НДС * @return $this - * @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС + * @throws TooManyVatsException Слишком много ставок НДС */ - public function addVat(Vat $vat) + public function addVat(Vat $vat): Document { $this->vats->add($vat); return $this; } - + /** * Возвращает массив ставок НДС * - * @return \AtolOnline\Entities\Vat[] + * @return Vat[] */ public function getVats(): array { return $this->vats->get(); } - + /** * Устанавливает массив ставок НДС * - * @param \AtolOnline\Entities\Vat[] $vats Массив ставок НДС + * @param Vat[] $vats Массив ставок НДС * @return $this - * @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС - * @throws \Exception + * @throws TooManyVatsException Слишком много ставок НДС + * @throws Exception */ - public function setVats(array $vats) + public function setVats(array $vats): Document { $this->vats->set($vats); return $this; } - + /** * Добавляет новую оплату в массив оплат * - * @param \AtolOnline\Entities\Payment $payment Объект оплаты + * @param Payment $payment Объект оплаты * @return $this - * @throws \Exception - * @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат + * @throws Exception + * @throws TooManyPaymentsException Слишком много оплат */ - public function addPayment(Payment $payment) + public function addPayment(Payment $payment): Document { if (count($this->getPayments()) == 0 && !$payment->getSum()) { $payment->setSum($this->calcTotal()); @@ -137,61 +152,61 @@ class Document extends Entity $this->payments->add($payment); return $this; } - + /** * Возвращает массив оплат * - * @return \AtolOnline\Entities\Payment[] + * @return Payment[] */ public function getPayments(): array { return $this->payments->get(); } - + /** * Устанавливает массив оплат * - * @param \AtolOnline\Entities\Payment[] $payments Массив оплат + * @param Payment[] $payments Массив оплат * @return $this - * @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException Слишком много оплат + * @throws TooManyPaymentsException Слишком много оплат */ - public function setPayments(array $payments) + public function setPayments(array $payments): Document { $this->payments->set($payments); return $this; } - + /** * Добавляет новый предмет расчёта в массив предметов расчёта * - * @param \AtolOnline\Entities\Item $item Объект предмета расчёта + * @param Item $item Объект предмета расчёта * @return $this - * @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта + * @throws TooManyItemsException Слишком много предметов расчёта */ - public function addItem(Item $item) + public function addItem(Item $item): Document { $this->items->add($item); return $this; } - + /** * Возвращает массив предметов расчёта * - * @return \AtolOnline\Entities\Item[] + * @return Item[] */ public function getItems(): array { return $this->items->get(); } - + /** * Устанавливает массив предметов расчёта * - * @param \AtolOnline\Entities\Item[] $items Массив предметов расчёта + * @param Item[] $items Массив предметов расчёта * @return $this - * @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта + * @throws TooManyItemsException Слишком много предметов расчёта */ - public function setItems(array $items) + public function setItems(array $items): Document { $this->items->set($items); return $this; @@ -213,7 +228,7 @@ class Document extends Entity * @param Client|null $client * @return $this */ - public function setClient(?Client $client) + public function setClient(?Client $client): Document { $this->client = $client; return $this; @@ -235,7 +250,7 @@ class Document extends Entity * @param Company|null $company * @return $this */ - public function setCompany(?Company $company) + public function setCompany(?Company $company): Document { $this->company = $company; return $this; @@ -250,55 +265,55 @@ class Document extends Entity { return $this->cashier; } - + /** * Устанавливает ФИО кассира. Тег ФФД - 1021. * * @param string|null $cashier * @return $this - * @throws \AtolOnline\Exceptions\AtolCashierTooLongException + * @throws TooLongCashierException */ - public function setCashier(?string $cashier) + public function setCashier(?string $cashier): Document { if ($cashier !== null) { $cashier = trim($cashier); - if (valid_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) { - throw new AtolCashierTooLongException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME); + if (mb_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) { + throw new TooLongCashierException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME); } } $this->cashier = $cashier; return $this; } - + /** * Возвращает данные коррекции * - * @return \AtolOnline\Entities\CorrectionInfo|null + * @return CorrectionInfo|null */ public function getCorrectionInfo(): ?CorrectionInfo { return $this->correction_info; } - + /** * Устанавливает данные коррекции * - * @param \AtolOnline\Entities\CorrectionInfo|null $correction_info + * @param CorrectionInfo|null $correction_info * @return $this */ - public function setCorrectionInfo(?CorrectionInfo $correction_info) + public function setCorrectionInfo(?CorrectionInfo $correction_info): Document { $this->correction_info = $correction_info; return $this; } - + /** * Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020. * * @return float - * @throws \Exception + * @throws Exception */ - public function calcTotal() + public function calcTotal(): float { $sum = 0; $this->clearVats(); @@ -318,32 +333,33 @@ class Document extends Entity { return $this->total; } - + /** * Собирает объект документа из сырой json-строки * * @param string $json - * @return \AtolOnline\Entities\Document - * @throws \AtolOnline\Exceptions\AtolEmailTooLongException - * @throws \AtolOnline\Exceptions\AtolEmailValidateException - * @throws \AtolOnline\Exceptions\AtolException - * @throws \AtolOnline\Exceptions\AtolInnWrongLengthException - * @throws \AtolOnline\Exceptions\AtolInvalidJsonException - * @throws \AtolOnline\Exceptions\AtolNameTooLongException - * @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException - * @throws \AtolOnline\Exceptions\AtolPhoneTooLongException - * @throws \AtolOnline\Exceptions\AtolPriceTooHighException - * @throws \AtolOnline\Exceptions\AtolTooManyException - * @throws \AtolOnline\Exceptions\AtolTooManyItemsException - * @throws \AtolOnline\Exceptions\AtolTooManyPaymentsException - * @throws \AtolOnline\Exceptions\AtolUnitTooLongException - * @throws \AtolOnline\Exceptions\AtolUserdataTooLongException + * @return Document + * @throws TooLongEmailException + * @throws InvalidEmailException + * @throws AtolException + * @throws InvalidInnLengthException + * @throws InvalidJsonException + * @throws TooLongNameException + * @throws TooLongPaymentAddressException + * @throws TooLongPhoneException + * @throws TooHighPriceException + * @throws BasicTooManyException + * @throws TooManyItemsException + * @throws TooManyPaymentsException + * @throws TooLongUnitException + * @throws TooLongUserdataException + * @throws Exception */ - public static function fromRaw(string $json) + public static function fromRaw(string $json): Document { $array = json_decode($json, true); if (json_last_error() !== JSON_ERROR_NONE) { - throw new AtolInvalidJsonException(); + throw new InvalidJsonException(); } $doc = new self(); if (isset($array['company'])) { @@ -416,13 +432,13 @@ class Document extends Entity } return $doc; } - + /** * Возвращает массив для кодирования в json * - * @throws \Exception + * @throws Exception */ - public function jsonSerialize() + public function jsonSerialize(): array { if ($this->getCompany()) { $json['company'] = $this->getCompany()->jsonSerialize(); // обязательно diff --git a/src/AtolOnline/Entities/Entity.php b/src/Entities/Entity.php similarity index 55% rename from src/AtolOnline/Entities/Entity.php rename to src/Entities/Entity.php index 0a35c9d..0b47b2d 100644 --- a/src/AtolOnline/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -1,25 +1,28 @@ name; } - + /** * Устаналивает наименование. Тег ФФД - 1030. * * @param string $name Наименование * @return $this - * @throws AtolNameTooLongException Слишком длинное имя/наименование + * @throws TooLongNameException Слишком длинное имя/наименование */ - public function setName(string $name) + public function setName(string $name): self { $name = trim($name); - if (valid_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) { - throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_ITEM_NAME); + if (mb_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) { + throw new TooLongNameException($name, Constraints::MAX_LENGTH_ITEM_NAME); } $this->name = $name; return $this; @@ -151,24 +150,24 @@ class Item extends Entity * * @return float */ - public function getPrice() + public function getPrice(): float { - return self::toRub($this->price); + return rubles($this->price); } - + /** * Устанавливает цену в рублях. Тег ФФД - 1079. * * @param float $rubles Цена за одну единицу в рублях * @return $this - * @throws AtolPriceTooHighException Слишком высокая цена за одну единицу + * @throws TooHighPriceException Слишком высокая цена за одну единицу */ - public function setPrice(float $rubles) + public function setPrice(float $rubles): Item { if ($rubles > 42949672.95) { - throw new AtolPriceTooHighException($rubles, 42949672.95); + throw new TooHighPriceException($rubles, 42949672.95); } - $this->price = self::toKop($rubles); + $this->price = kopeks($rubles); $this->calcSum(); return $this; } @@ -182,22 +181,22 @@ class Item extends Entity { return $this->quantity; } - + /** * Устанавливает количество. Тег ФФД - 1023. * - * @param float $quantity Количество + * @param float $quantity Количество * @param string|null $measurement_unit Единица измерения количества * @return $this - * @throws AtolTooManyException Слишком большое количество - * @throws AtolPriceTooHighException Слишком высокая общая стоимость - * @throws AtolUnitTooLongException Слишком длинное название единицы измерения + * @throws BasicTooManyException Слишком большое количество + * @throws TooHighPriceException Слишком высокая общая стоимость + * @throws TooLongUnitException Слишком длинное название единицы измерения */ - public function setQuantity(float $quantity, string $measurement_unit = null) + public function setQuantity(float $quantity, string $measurement_unit = null): Item { $quantity = round($quantity, 3); if ($quantity > 99999.999) { - throw new AtolTooManyException($quantity, 99999.999); + throw new BasicTooManyException($quantity, 99999.999); } $this->quantity = $quantity; $this->calcSum(); @@ -216,19 +215,19 @@ class Item extends Entity { return $this->measurement_unit; } - + /** * Устанавливает единицу измерения количества. Тег ФФД - 1197. * * @param string $measurement_unit Единица измерения количества * @return $this - * @throws AtolUnitTooLongException Слишком длинное название единицы измерения + * @throws TooLongUnitException Слишком длинное название единицы измерения */ - public function setMeasurementUnit(string $measurement_unit) + public function setMeasurementUnit(string $measurement_unit): Item { $measurement_unit = trim($measurement_unit); - if (valid_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) { - throw new AtolUnitTooLongException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT); + if (mb_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) { + throw new TooLongUnitException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT); } $this->measurement_unit = $measurement_unit; return $this; @@ -251,7 +250,7 @@ class Item extends Entity * @return $this * @todo Проверка допустимых значений */ - public function setPaymentMethod(string $payment_method) + public function setPaymentMethod(string $payment_method): Item { $this->payment_method = trim($payment_method); return $this; @@ -274,30 +273,30 @@ class Item extends Entity * @return $this * @todo Проверка допустимых значений */ - public function setPaymentObject(string $payment_object) + public function setPaymentObject(string $payment_object): Item { $this->payment_object = trim($payment_object); return $this; } - + /** * Возвращает ставку НДС * - * @return \AtolOnline\Entities\Vat|null + * @return Vat|null */ public function getVat(): ?Vat { return $this->vat; } - + /** * Устанавливает ставку НДС * * @param string|null $vat_type Тип ставки НДС. Передать null, чтобы удалить ставку. * @return $this - * @throws \AtolOnline\Exceptions\AtolPriceTooHighException + * @throws TooHighPriceException */ - public function setVatType(?string $vat_type) + public function setVatType(?string $vat_type): Item { if ($vat_type) { $this->vat @@ -319,19 +318,19 @@ class Item extends Entity { return $this->user_data; } - + /** * Устанавливает дополнительный реквизит. Тег ФФД - 1191. * * @param string $user_data Дополнительный реквизит. Тег ФФД - 1191. * @return $this - * @throws AtolUserdataTooLongException Слишком длинный дополнительный реквизит + * @throws TooLongUserdataException Слишком длинный дополнительный реквизит */ - public function setUserData(string $user_data) + public function setUserData(string $user_data): self { $user_data = trim($user_data); - if (valid_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) { - throw new AtolUserdataTooLongException($user_data, Constraints::MAX_LENGTH_USER_DATA); + if (mb_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) { + throw new TooLongUserdataException($user_data, Constraints::MAX_LENGTH_USER_DATA); } $this->user_data = $user_data; return $this; @@ -344,24 +343,24 @@ class Item extends Entity */ public function getSum(): float { - return self::toRub($this->sum); + return rubles($this->sum); } - + /** * Расчитывает стоимость и размер НДС на неё * * @return float - * @throws AtolPriceTooHighException Слишком большая сумма + * @throws TooHighPriceException Слишком большая сумма */ - public function calcSum() + public function calcSum(): float { $sum = $this->quantity * $this->price; - if (self::toRub($sum) > 42949672.95) { - throw new AtolPriceTooHighException($sum, 42949672.95); + if (rubles($sum) > 42949672.95) { + throw new TooHighPriceException($sum, 42949672.95); } $this->sum = $sum; if ($this->vat) { - $this->vat->setSum(self::toRub($sum)); + $this->vat->setSum(rubles($sum)); } return $this->getSum(); } diff --git a/src/AtolOnline/Entities/ItemArray.php b/src/Entities/ItemArray.php similarity index 78% rename from src/AtolOnline/Entities/ItemArray.php rename to src/Entities/ItemArray.php index 059f243..09ee0dc 100644 --- a/src/AtolOnline/Entities/ItemArray.php +++ b/src/Entities/ItemArray.php @@ -1,15 +1,17 @@ set($items); } } - + /** * Устанавливает массив предметов расчёта * * @param Item[] $items Массив предметов расчёта * @return $this - * @throws AtolTooManyItemsException Слишком много предметов расчёта + * @throws TooManyItemsException Слишком много предметов расчёта */ - public function set(array $items) + public function set(array $items): ItemArray { if ($this->validateCount($items)) { $this->items = $items; } return $this; } - + /** * Добавляет предмет расчёта в массив * * @param Item $item Объект предмета расчёта * @return $this - * @throws AtolTooManyItemsException Слишком много предметов расчёта + * @throws TooManyItemsException Слишком много предметов расчёта */ - public function add(Item $item) + public function add(Item $item): ItemArray { if ($this->validateCount()) { $this->items[] = $item; @@ -78,7 +80,7 @@ class ItemArray extends Entity * * @return Item[] */ - public function get() + public function get(): array { return $this->items; } @@ -94,19 +96,19 @@ class ItemArray extends Entity } return $result; } - + /** * Проверяет количество предметов расчёта * * @param Item[]|null $items Если передать массив, то проверит количество его элементов. * Иначе проверит количество уже присвоенных элементов. * @return bool true если всё хорошо, иначе выбрасывает исключение - * @throws AtolTooManyItemsException Слишком много предметов расчёта + * @throws TooManyItemsException Слишком много предметов расчёта */ protected function validateCount(?array $items = null): bool { if ((!empty($items) && count($items) >= self::MAX_COUNT) || count($this->items) >= self::MAX_COUNT) { - throw new AtolTooManyItemsException(count($items), self::MAX_COUNT); + throw new TooManyItemsException(count($items), self::MAX_COUNT); } return true; } diff --git a/src/AtolOnline/Entities/Payment.php b/src/Entities/Payment.php similarity index 88% rename from src/AtolOnline/Entities/Payment.php rename to src/Entities/Payment.php index 27f8398..0a3d356 100644 --- a/src/AtolOnline/Entities/Payment.php +++ b/src/Entities/Payment.php @@ -1,12 +1,14 @@ type = $type; return $this; @@ -78,7 +80,7 @@ class Payment extends Entity * @param float $sum * @return $this */ - public function setSum(float $sum) + public function setSum(float $sum): Payment { $this->sum = $sum; return $this; diff --git a/src/AtolOnline/Entities/PaymentArray.php b/src/Entities/PaymentArray.php similarity index 77% rename from src/AtolOnline/Entities/PaymentArray.php rename to src/Entities/PaymentArray.php index f30920a..a92997a 100644 --- a/src/AtolOnline/Entities/PaymentArray.php +++ b/src/Entities/PaymentArray.php @@ -1,15 +1,17 @@ set($payments); } } - + /** * Устанавливает массив оплат * * @param Payment[] $payments * @return $this - * @throws AtolTooManyPaymentsException Слишком много оплат + * @throws TooManyPaymentsException Слишком много оплат */ - public function set(array $payments) + public function set(array $payments): PaymentArray { if ($this->validateCount($payments)) { $this->payments = $payments; } return $this; } - + /** * Добавляет новую оплату к заданным * * @param Payment $payment Объект оплаты * @return $this - * @throws AtolTooManyPaymentsException Слишком много оплат + * @throws TooManyPaymentsException Слишком много оплат */ - public function add(Payment $payment) + public function add(Payment $payment): PaymentArray { if ($this->validateCount()) { $this->payments[] = $payment; @@ -76,7 +78,7 @@ class PaymentArray extends Entity * * @return Payment[] */ - public function get() + public function get(): array { return $this->payments; } @@ -92,19 +94,19 @@ class PaymentArray extends Entity } return $result; } - + /** * Проверяет количество налоговых ставок * * @param Payment[]|null $payments Если передать массив, то проверит количество его элементов. * Иначе проверит количество уже присвоенных элементов. * @return bool true если всё хорошо, иначе выбрасывает исключение - * @throws AtolTooManyPaymentsException Слишком много оплат + * @throws TooManyPaymentsException Слишком много оплат */ protected function validateCount(?array $payments = null): bool { if ((!empty($payments) && count($payments) >= self::MAX_COUNT) || count($this->payments) >= self::MAX_COUNT) { - throw new AtolTooManyPaymentsException(count($payments), self::MAX_COUNT); + throw new TooManyPaymentsException(count($payments), self::MAX_COUNT); } return true; } diff --git a/src/AtolOnline/Entities/Vat.php b/src/Entities/Vat.php similarity index 87% rename from src/AtolOnline/Entities/Vat.php rename to src/Entities/Vat.php index bd76ca1..98dd642 100644 --- a/src/AtolOnline/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -1,15 +1,17 @@ type = $type; $this->setFinal(); @@ -111,9 +111,9 @@ class Vat extends Entity * * @return float */ - public function getFinalSum() + public function getFinalSum(): float { - return self::toRub($this->sum_final); + return rubles($this->sum_final); } /** @@ -123,9 +123,9 @@ class Vat extends Entity * @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС * @return $this */ - public function setSum(float $rubles) + public function setSum(float $rubles): Vat { - $this->sum_original = self::toKop($rubles); + $this->sum_original = kopeks($rubles); $this->setFinal(); return $this; } @@ -137,7 +137,7 @@ class Vat extends Entity */ public function getSum(): float { - return self::toRub($this->sum_original); + return rubles($this->sum_original); } /** @@ -147,9 +147,9 @@ class Vat extends Entity * @param float $rubles * @return $this */ - public function addSum(float $rubles) + public function addSum(float $rubles): Vat { - $this->sum_original += self::toKop($rubles); + $this->sum_original += kopeks($rubles); $this->setFinal(); return $this; } @@ -163,7 +163,7 @@ class Vat extends Entity */ public function calc(float $rubles): float { - return self::toRub(self::calculator($this->type, self::toKop($rubles))); + return rubles(self::calculator($this->type, kopeks($rubles))); } /** @@ -180,7 +180,7 @@ class Vat extends Entity /** * Расчитывает и устанавливает итоговый размер ставки от исходной суммы в копейках */ - protected function setFinal() + protected function setFinal(): Vat { $this->sum_final = self::calculator($this->type, $this->sum_original); return $this; diff --git a/src/AtolOnline/Entities/VatArray.php b/src/Entities/VatArray.php similarity index 78% rename from src/AtolOnline/Entities/VatArray.php rename to src/Entities/VatArray.php index c292127..db92ca7 100644 --- a/src/AtolOnline/Entities/VatArray.php +++ b/src/Entities/VatArray.php @@ -1,15 +1,17 @@ set($vats); } } - + /** * Устанавливает массив ставок НДС * * @param Vat[] $vats Массив ставок НДС * @return $this - * @throws AtolTooManyVatsException Слишком много ставок НДС + * @throws TooManyVatsException Слишком много ставок НДС */ - public function set(array $vats) + public function set(array $vats): VatArray { if ($this->validateCount($vats)) { $this->vats = $vats; } return $this; } - + /** * Добавляет новую ставку НДС в массив * * @param Vat $vat Объект ставки НДС * @return $this - * @throws AtolTooManyVatsException Слишком много ставок НДС + * @throws TooManyVatsException Слишком много ставок НДС */ - public function add(Vat $vat) + public function add(Vat $vat): VatArray { if ($this->validateCount()) { if (isset($this->vats[$vat->getType()])) { @@ -80,7 +82,7 @@ class VatArray extends Entity * * @return Vat[] */ - public function get() + public function get(): array { return $this->vats; } @@ -96,19 +98,19 @@ class VatArray extends Entity } return $result; } - + /** * Проверяет количество налоговых ставок * * @param Vat[]|null $vats Если передать массив, то проверит количество его элементов. * Иначе проверит количество уже присвоенных элементов. * @return bool true если всё хорошо, иначе выбрасывает исключение - * @throws AtolTooManyVatsException Слишком много ставок НДС + * @throws TooManyVatsException Слишком много ставок НДС */ protected function validateCount(?array $vats = null): bool { if ((!empty($vats) && count($vats) >= self::MAX_COUNT) || count($this->vats) >= self::MAX_COUNT) { - throw new AtolTooManyVatsException(count($vats), self::MAX_COUNT); + throw new TooManyVatsException(count($vats), self::MAX_COUNT); } return true; } diff --git a/src/AtolOnline/Exceptions/AtolException.php b/src/Exceptions/AtolException.php similarity index 75% rename from src/AtolOnline/Exceptions/AtolException.php rename to src/Exceptions/AtolException.php index dbf4d18..b548ab6 100644 --- a/src/AtolOnline/Exceptions/AtolException.php +++ b/src/Exceptions/AtolException.php @@ -1,12 +1,14 @@ ffd_tags; } diff --git a/src/AtolOnline/Exceptions/AtolAuthFailedException.php b/src/Exceptions/AuthFailedException.php similarity index 55% rename from src/AtolOnline/Exceptions/AtolAuthFailedException.php rename to src/Exceptions/AuthFailedException.php index 5cf16b3..9c2c76a 100644 --- a/src/AtolOnline/Exceptions/AtolAuthFailedException.php +++ b/src/Exceptions/AuthFailedException.php @@ -1,12 +1,14 @@ isValid() ? $message - : '['.$last_response->error->code.'] '.$last_response->error->text. - '. ERROR_ID: '.$last_response->error->error_id. - '. TYPE: '.$last_response->error->type; + : '[' . $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); } diff --git a/src/AtolOnline/Exceptions/AtolTooLongException.php b/src/Exceptions/BasicTooLongException.php similarity index 55% rename from src/AtolOnline/Exceptions/AtolTooLongException.php rename to src/Exceptions/BasicTooLongException.php index eb56c53..d27905b 100644 --- a/src/AtolOnline/Exceptions/AtolTooLongException.php +++ b/src/Exceptions/BasicTooLongException.php @@ -1,40 +1,40 @@ message.' (max length - '.$max.', actual length - '. - valid_strlen($string), $code, $previous); + parent::__construct($message ?: $this->message . ' (max length - ' . $max . ', actual length - ' . + mb_strlen($string), $code, $previous); } } \ No newline at end of file diff --git a/src/AtolOnline/Exceptions/AtolTooManyException.php b/src/Exceptions/BasicTooManyException.php similarity index 50% rename from src/AtolOnline/Exceptions/AtolTooManyException.php rename to src/Exceptions/BasicTooManyException.php index 427c96b..7f4dd3e 100644 --- a/src/AtolOnline/Exceptions/AtolTooManyException.php +++ b/src/Exceptions/BasicTooManyException.php @@ -1,40 +1,43 @@ message.' (max - '.$max.', actual - '.$quantity.')'; - parent::__construct($message, $code, $previous); + parent::__construct( + $message ?: $this->message . ' (max - ' . $max . ', actual - ' . $quantity . ')', + $code, + $previous + ); } } \ No newline at end of file diff --git a/src/AtolOnline/Exceptions/AtolCorrectionInfoException.php b/src/Exceptions/EmptyCorrectionInfoException.php similarity index 76% rename from src/AtolOnline/Exceptions/AtolCorrectionInfoException.php rename to src/Exceptions/EmptyCorrectionInfoException.php index bda765b..f0b2f1f 100644 --- a/src/AtolOnline/Exceptions/AtolCorrectionInfoException.php +++ b/src/Exceptions/EmptyCorrectionInfoException.php @@ -1,20 +1,20 @@ 'https://testonline.atol.ru/possystem/v4/', + 'company_name' => 'АТОЛ', + 'inn' => '5544332219', + 'payment_address' => 'https://v4.online.atol.ru', + 'group' => 'v4-online-atol-ru_4179', + 'login' => 'v4-online-atol-ru', + 'password' => 'iGFFuihss', + 'endpoint_ofd' => 'https://consumer.1-ofd-test.ru/v1', + ]; + } + + /** + * Возвращает данные для работы с тестовой средой АТОЛ Онлайн ФФД 1.2 + * + * @return string[] + */ + public static function FFD12(): array + { + return [ + 'endpoint' => 'https://testonline.atol.ru/possystem/v5/', + 'company_name' => 'АТОЛ', + 'inn' => '5544332219', + 'payment_address' => 'https://v5.online.atol.ru', + 'group' => 'v5-online-atol-ru_5179', + 'login' => 'v5-online-atol-ru', + 'password' => 'zUr0OxfI', + 'endpoint_ofd' => '', + ]; + } +} \ No newline at end of file diff --git a/src/helpers.php b/src/helpers.php deleted file mode 100644 index c4f0900..0000000 --- a/src/helpers.php +++ /dev/null @@ -1,16 +0,0 @@ -assertIsObject($entity); + $this->assertIsObject($entity->jsonSerialize()); + $this->assertIsString((string)$entity); $this->assertJson((string)$entity); - return $this; - } - - /** - * - */ - public function tearDown(): void - { - //parent::tearDown(); - } - - /** - * Возвращает случайную строку указанной длины - * - * @param int $length - * @return string - */ - protected static function randomString($length = 8) - { - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $string = ''; - for ($i = 0; $i < $length; $i++) { - $string .= $characters[mt_rand(0, strlen($characters) - 1)]; + if ($json_structure) { + $this->assertEquals(json_encode($json_structure), (string)$entity); } - return $string; + } + + /** + * Провайдер валидных телефонов + * + * @return array> + */ + public function providerValidPhones(): array + { + return [ + ['+79991234567', '+79991234567'], + ['79991234567', '+79991234567'], + ['89991234567', '+89991234567'], + ['+7 999 123 45 67', '+79991234567'], + ['+7 (999) 123-45-67', '+79991234567'], + ["+7 %(?9:9\"9')abc\r123\n45\t67\0", '+79991234567'], + ]; + } + + /** + * Провайдер валидных email-ов + * + * @return array> + */ + public function providerValidEmails(): array + { + return [ + ['abc@mail.com'], + ['abc-d@mail.com'], + ['abc.def@mail.com'], + ['abc.def@mail.org'], + ['abc.def@mail-archive.com'], + ]; } } \ No newline at end of file diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..8447c27 --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,317 @@ +> + */ + public function providerNullableStrings(): array + { + return [ + [''], + [' '], + [null], + ["\n\r\t"], + ]; + } + + /** + * Провайдер телефонов, которые приводятся к null + * + * @return array> + */ + public function providerNullablePhones(): array + { + return array_merge( + $this->providerNullableStrings(), + [ + [Helpers::randomStr(10, false)], + ["asdfgvs \n\rtt\t*/(*&%^*$%"], + ] + ); + } + + /** + * Провайдер невалидных email-ов + * + * @return array> + */ + public function providerInvalidEmails(): array + { + return [ + ['@example'], + [Helpers::randomStr(15)], + ['@example.com'], + ['abc.def@mail'], + ['.abc@mail.com'], + ['example@example'], + ['abc..def@mail.com'], + ['abc.def@mail..com'], + ['abc.def@mail#archive.com'], + ]; + } + + //------------------------------------------------------------------------------------------------------------------ + + /** + * Тестирует приведение покупателя к json + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::jsonSerialize + */ + public function testAtolable(): void + { + $this->assertAtolable(new Client()); + } + + /** + * Тестирует конструктор покупателя без передачи значений + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::jsonSerialize + */ + public function testConstructorWithoutArgs(): void + { + $this->assertEquals('{}', (string)(new Client())); + } + + /** + * Тестирует конструктор с передачей значений (внутри работают сеттеры) + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::jsonSerialize + * @covers \AtolOnline\Entities\Client::setName + * @covers \AtolOnline\Entities\Client::setPhone + * @covers \AtolOnline\Entities\Client::setEmail + * @covers \AtolOnline\Entities\Client::setInn + */ + public function testConstructorWithArgs(): void + { + $customer = new Client( + 'John Doe', + 'john@example.com', + '+1/22/99*73s dsdas654 5s6', // +122997365456 + '+fasd3\qe3fs_=nac99013928czc' // 3399013928 + ); + $this->assertAtolable($customer, [ + 'name' => 'John Doe', + 'email' => 'john@example.com', + 'phone' => '+122997365456', + 'inn' => '3399013928', + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + + /** + * Тестирует установку имён покупателя, которые приводятся к null + * + * @param mixed $name + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setName + * @covers \AtolOnline\Entities\Client::getName + * @throws TooLongNameException + */ + public function testNullableNames(mixed $name): void + { + $customer = (new Client())->setName($name); + $this->assertNull($customer->getName()); + } + + /** + * Тестирует установку валидного имени покупателя + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setName + * @covers \AtolOnline\Entities\Client::getName + * @throws TooLongNameException + */ + public function testValidName(): void + { + $name = Helpers::randomStr(); + $customer = (new Client())->setName($name); + $this->assertEquals($name, $customer->getName()); + } + + /** + * Тестирует установку невалидного имени покупателя + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setName + * @covers \AtolOnline\Exceptions\TooLongNameException + * @throws TooLongNameException + */ + public function testInvalidName(): void + { + $this->expectException(TooLongNameException::class); + (new Client())->setName(Helpers::randomStr(400)); + } + + //------------------------------------------------------------------------------------------------------------------ + + /** + * Тестирует установку телефонов покупателя, которые приводятся к null + * + * @param mixed $phone + * @dataProvider providerNullablePhones + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setPhone + * @covers \AtolOnline\Entities\Client::getPhone + * @throws TooLongPhoneException + */ + public function testNullablePhones(mixed $phone): void + { + $customer = (new Client())->setPhone($phone); + $this->assertNull($customer->getPhone()); + } + + /** + * Тестирует установку валидного телефона покупателя + * + * @dataProvider providerValidPhones + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setPhone + * @covers \AtolOnline\Entities\Client::getPhone + * @throws TooLongPhoneException + */ + public function testValidPhone(string $input, string $output): void + { + $customer = (new Client())->setPhone($input); + $this->assertEquals($output, $customer->getPhone()); + } + + /** + * Тестирует установку невалидного телефона покупателя + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setPhone + * @covers \AtolOnline\Exceptions\TooLongPhoneException + * @throws TooLongPhoneException + */ + public function testInvalidPhone(): void + { + $this->expectException(TooLongPhoneException::class); + (new Client())->setPhone('99999999999999999999999999999999999999999999999999999999999999999999999999'); + } + + //------------------------------------------------------------------------------------------------------------------ + + /** + * Тестирует установку валидных email-ов покупателя + * + * @param mixed $email + * @dataProvider providerValidEmails + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setEmail + * @covers \AtolOnline\Entities\Client::getEmail + * @throws TooLongEmailException + * @throws InvalidEmailException + */ + public function testValidEmails(mixed $email): void + { + $customer = (new Client())->setEmail($email); + $this->assertEquals($email, $customer->getEmail()); + } + + /** + * Тестирует установку слишком длинного email покупателя + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setEmail + * @covers \AtolOnline\Exceptions\TooLongEmailException + * @throws TooLongEmailException + * @throws InvalidEmailException + */ + public function testTooLongEmail(): void + { + $this->expectException(TooLongEmailException::class); + (new Client())->setEmail(Helpers::randomStr(65)); + } + + /** + * Тестирует установку невалидного email покупателя + * + * @param mixed $email + * @dataProvider providerInvalidEmails + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setEmail + * @covers \AtolOnline\Exceptions\InvalidEmailException + * @throws TooLongEmailException + * @throws InvalidEmailException + */ + public function testInvalidEmail(mixed $email): void + { + $this->expectException(InvalidEmailException::class); + (new Client())->setEmail($email); + } + + //------------------------------------------------------------------------------------------------------------------ + + /** + * Тестирует исключение о корректной длине ИНН + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setInn + * @covers \AtolOnline\Entities\Client::getInn + * @throws InvalidInnLengthException + */ + public function testValidInn(): void + { + $customer = (new Client())->setInn('1234567890'); + $this->assertEquals('1234567890', $customer->getInn()); + $customer = $customer->setInn('123456789012'); + $this->assertEquals('123456789012', $customer->getInn()); + } + + /** + * Тестирует исключение о некорректной длине ИНН (10 цифр) + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidInnLengthException + */ + public function testInvalidInn10(): void + { + $this->expectException(InvalidInnLengthException::class); + (new Client())->setInn('12345678901'); + } + + /** + * Тестирует исключение о некорректной длине ИНН (12 цифр) + * + * @covers \AtolOnline\Entities\Client + * @covers \AtolOnline\Entities\Client::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidInnLengthException + */ + public function testInvalidInn12(): void + { + $this->expectException(InvalidInnLengthException::class); + (new Client())->setInn('1234567890123'); + } +} \ No newline at end of file diff --git a/tests/CompanyTest.php b/tests/CompanyTest.php new file mode 100644 index 0000000..cad4c86 --- /dev/null +++ b/tests/CompanyTest.php @@ -0,0 +1,134 @@ +assertAtolable(new Company( + $email = 'company@example.com', + $sno = SnoTypes::OSN, + $inn = '1234567890', + $payment_address = 'https://example.com', + ), [ + 'email' => $email, + 'sno' => $sno, + 'inn' => $inn, + 'payment_address' => $payment_address, + ]); + } + + /** + * Тестирует исключение о слишком длинном email + * + * @covers \AtolOnline\Entities\Company + * @covers \AtolOnline\Entities\Company::setEmail + * @covers \AtolOnline\Exceptions\TooLongEmailException + */ + public function testEmailTooLongException() + { + $this->expectException(TooLongEmailException::class); + new Company(Helpers::randomStr(65), SnoTypes::OSN, '1234567890', 'https://example.com'); + } + + /** + * Тестирует исключение о невалидном email + * + * @covers \AtolOnline\Entities\Company + * @covers \AtolOnline\Entities\Company::setEmail + * @covers \AtolOnline\Exceptions\InvalidEmailException + */ + public function testInvalidEmailException() + { + $this->expectException(InvalidEmailException::class); + new Company('company@examas%^*.com', SnoTypes::OSN, '1234567890', 'https://example.com'); + } + + /** + * Тестирует исключение о слишком длинном платёжном адресе + * + * @covers \AtolOnline\Entities\Company + * @covers \AtolOnline\Entities\Company::setSno + * @covers \AtolOnline\Exceptions\InvalidSnoException + */ + public function testInvalidSnoException() + { + $this->expectException(InvalidSnoException::class); + new Company('company@example.com', 'test', '1234567890', 'https://example.com'); + } + + /** + * Тестирует исключение о слишком длинном платёжном адресе + * + * @covers \AtolOnline\Entities\Company + * @covers \AtolOnline\Entities\Company::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + */ + public function testInvalidInnLengthException() + { + $this->expectException(InvalidInnLengthException::class); + new Company('company@example.com', SnoTypes::OSN, Helpers::randomStr(13), 'https://example.com'); + } + + /** + * Тестирует исключение о слишком длинном платёжном адресе + * + * @covers \AtolOnline\Entities\Company + * @covers \AtolOnline\Entities\Company::setPaymentAddress + * @covers \AtolOnline\Exceptions\TooLongPaymentAddressException + */ + public function testTooLongPaymentAddressException() + { + $this->expectException(TooLongPaymentAddressException::class); + new Company('company@example.com', SnoTypes::OSN, '1234567890', Helpers::randomStr(257)); + } + + /** + * Тестирует исключение о невалидном платёжном адресе + * + * @covers \AtolOnline\Entities\Company + * @covers \AtolOnline\Entities\Company::setPaymentAddress + * @covers \AtolOnline\Exceptions\InvalidPaymentAddressException + */ + public function testInvalidPaymentAddressException() + { + $this->expectException(InvalidPaymentAddressException::class); + new Company('company@example.com', SnoTypes::OSN, '1234567890', ''); + } +} \ No newline at end of file diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php new file mode 100644 index 0000000..1259886 --- /dev/null +++ b/tests/HelpersTest.php @@ -0,0 +1,123 @@ +> + */ + public function providerKopeksToRubles(): array + { + return [ + [null, 0], + [0, 0], + [1, 0.01], + [12, 0.12], + [123, 1.23], + [1234, 12.34], + [12345, 123.45], + [-1, 0.01], + [-12, 0.12], + [-123, 1.23], + [-1234, 12.34], + [-12345, 123.45], + ]; + } + + /** + * Провайдер рублей для перевода в копейки + * + * @return array> + */ + public function providerRublesToKopeks(): array + { + return [ + [null, 0], + [0, 0], + [0.01, 1], + [0.12, 12], + [1.23, 123], + [12.34, 1234], + [123.45, 12345], + [-0.01, 1], + [-0.12, 12], + [-1.23, 123], + [-12.34, 1234], + [-123.45, 12345], + ]; + } + + /** + * Провайдер для тестирования генерации рандомной строки + * + * @return array> + */ + public function providerRandomStr(): array + { + return [ + [0, 0], + [1, 1], + [5, 5], + [-1, 1], + [-5, 5], + ]; + } + + //------------------------------------------------------------------------------------------------------------------ + + /** + * Тестирует перевод копеек в рубли + * + * @dataProvider providerKopeksToRubles + * @covers \AtolOnline\Helpers::KopToRub + */ + public function testKopeksToRubles(?int $kopeks, float $rubles): void + { + $result = Helpers::KopToRub($kopeks); + $this->assertIsFloat($result); + $this->assertEquals($result, $rubles); + } + + /** + * Тестирует перевод копеек в рубли + * + * @dataProvider providerRublesToKopeks + * @covers \AtolOnline\Helpers::RubToKop + */ + public function testRublesToKopeks(?float $rubles, int $kopeks): void + { + $result = Helpers::RubToKop($rubles); + $this->assertIsInt($result); + $this->assertEquals($result, $kopeks); + } + + /** + * Тестирует длину рандомной строки + * + * @param int $input + * @param int $output + * @dataProvider providerRandomStr + */ + public function testRandomString(int $input, int $output): void + { + $result = Helpers::randomStr($input); + $this->assertIsString($result); + $this->assertEquals($output, strlen($result)); + // тестировать на наличие цифр быссмысленно + } +} \ No newline at end of file diff --git a/tests/Feature/ItemTest.php b/tests/ItemTest_todo.php similarity index 68% rename from tests/Feature/ItemTest.php rename to tests/ItemTest_todo.php index 0971910..59a8288 100644 --- a/tests/Feature/ItemTest.php +++ b/tests/ItemTest_todo.php @@ -1,34 +1,37 @@ checkAtolEntity($item); + $this->assertAtolable($item); $this->assertEquals('Банан', $item->getName()); $this->assertEquals(65.99, $item->getPrice()); $this->assertEquals(2.74, $item->getQuantity()); @@ -50,15 +53,15 @@ class ItemTest extends BasicTestCase $this->assertEquals(PaymentObjects::COMMODITY, $item->getPaymentObject()); $this->assertEquals(PaymentMethods::FULL_PAYMENT, $item->getPaymentMethod()); } - + /** * Тестирует установку параметров через сеттеры * - * @throws AtolOnline\Exceptions\AtolNameTooLongException - * @throws AtolOnline\Exceptions\AtolPriceTooHighException - * @throws AtolOnline\Exceptions\AtolTooManyException - * @throws AtolOnline\Exceptions\AtolUnitTooLongException - * @throws AtolOnline\Exceptions\AtolUserdataTooLongException + * @throws AtolOnline\Exceptions\TooLongNameException + * @throws AtolOnline\Exceptions\TooHighPriceException + * @throws AtolOnline\Exceptions\BasicTooManyException + * @throws AtolOnline\Exceptions\TooLongUnitException + * @throws AtolOnline\Exceptions\TooLongUserdataException */ public function testSetters() { @@ -71,7 +74,7 @@ class ItemTest extends BasicTestCase $item->setPaymentObject(PaymentObjects::COMMODITY); $item->setPaymentMethod(PaymentMethods::FULL_PAYMENT); $item->setUserData('Some user data'); - $this->checkAtolEntity($item); + $this->assertAtolable($item); $this->assertEquals('Банан', $item->getName()); $this->assertEquals(65.99, $item->getPrice()); $this->assertEquals(2.74, $item->getQuantity()); @@ -81,11 +84,11 @@ class ItemTest extends BasicTestCase $this->assertEquals(PaymentMethods::FULL_PAYMENT, $item->getPaymentMethod()); $this->assertEquals('Some user data', $item->getUserData()); } - + /** * Тестирует установку ставки НДС разными путями * - * @throws \AtolOnline\Exceptions\AtolPriceTooHighException + * @throws TooHighPriceException */ public function testSetVat() { @@ -95,66 +98,66 @@ class ItemTest extends BasicTestCase $item->setVatType(VatTypes::VAT20); $this->assertEquals(VatTypes::VAT20, $item->getVat()->getType()); } - + /** * Тестирует исключение о слишком длинном наименовании * - * @throws \AtolOnline\Exceptions\AtolNameTooLongException + * @throws TooLongNameException */ public function testAtolNameTooLongException() { $item = new Item(); - $this->expectException(AtolNameTooLongException::class); - $item->setName(self::randomString(130)); + $this->expectException(TooLongNameException::class); + $item->setName(Helpers::randomStr(130)); } - + /** * Тестирует исключение о слишком высоком количестве * - * @throws \AtolOnline\Exceptions\AtolPriceTooHighException - * @throws \AtolOnline\Exceptions\AtolTooManyException - * @throws \AtolOnline\Exceptions\AtolUnitTooLongException + * @throws TooHighPriceException + * @throws BasicTooManyException + * @throws TooLongUnitException */ public function testAtolQuantityTooHighException() { $item = new Item(); - $this->expectException(AtolTooManyException::class); + $this->expectException(BasicTooManyException::class); $item->setQuantity(100000.1); } - + /** * Тестирует исключение о слишком высокой цене * - * @throws \AtolOnline\Exceptions\AtolPriceTooHighException + * @throws TooHighPriceException */ public function testAtolPriceTooHighException() { $item = new Item(); - $this->expectException(AtolPriceTooHighException::class); + $this->expectException(TooHighPriceException::class); $item->setPrice(42949673.1); } - + /** * Тестирует исключение о слишком длинных польз. данных * - * @throws \AtolOnline\Exceptions\AtolUserdataTooLongException + * @throws TooLongUserdataException */ public function testAtolUserdataTooLongException() { $item = new Item(); - $this->expectException(AtolUserdataTooLongException::class); + $this->expectException(TooLongUserdataException::class); $item->setUserData('User data User data User data User data User data User data User data'); } - + /** * Тестирует исключение о слишком длинной единице измерения * - * @throws \AtolOnline\Exceptions\AtolUnitTooLongException + * @throws TooLongUnitException */ public function testAtolUnitTooLongException() { $item = new Item(); - $this->expectException(AtolUnitTooLongException::class); + $this->expectException(TooLongUnitException::class); $item->setMeasurementUnit('кг кг кг кг кг кг кг кг кг '); } } \ No newline at end of file diff --git a/tests/Unit/ClientTest.php b/tests/Unit/ClientTest.php deleted file mode 100644 index 0df1a53..0000000 --- a/tests/Unit/ClientTest.php +++ /dev/null @@ -1,103 +0,0 @@ -checkAtolEntity($customer); - $this->assertEquals('John Doe', $customer->getName()); - $this->assertEquals('+122997365456', $customer->getPhone()); - $this->assertEquals('john@example.com', $customer->getEmail()); - $this->assertEquals('3399013928', $customer->getInn()); - } - - /** - * Тестирует исключение о слишком длинном имени - * - * @throws \AtolOnline\Exceptions\AtolNameTooLongException - */ - public function testAtolNameTooLongException() - { - $customer = new Client(); - $this->expectException(AtolNameTooLongException::class); - $customer->setName(self::randomString(257)); - } - - /** - * Тестирует исключение о слишком длинном телефоне - * - * @throws \AtolOnline\Exceptions\AtolPhoneTooLongException - */ - public function testAtolPhoneTooLongException() - { - $customer = new Client(); - $this->expectException(AtolPhoneTooLongException::class); - $customer->setPhone('99999999999999999999999999999999999999999999999999999999999999999999999999'); - } - - /** - * Тестирует исключение о слишком длинной почте - * - * @throws \AtolOnline\Exceptions\AtolEmailTooLongException - * @throws \AtolOnline\Exceptions\AtolEmailValidateException - */ - public function testAtolEmailTooLongException() - { - $customer = new Client(); - $this->expectException(AtolEmailTooLongException::class); - $customer->setEmail(self::randomString(65)); - } - - /** - * Тестирует исключение о некорректной почте - * - * @throws \AtolOnline\Exceptions\AtolEmailTooLongException - * @throws \AtolOnline\Exceptions\AtolEmailValidateException - */ - public function testAtolEmailValidateException() - { - $customer = new Client(); - $this->expectException(AtolEmailValidateException::class); - $customer->setEmail(self::randomString(15)); - } - - /** - * Тестирует исключение о некорректной длине ИНН - * - * @throws \AtolOnline\Exceptions\AtolInnWrongLengthException - */ - public function testAtolInnWrongLengthException() - { - $company = new Client(); - $this->expectException(AtolInnWrongLengthException::class); - $company->setInn('123456789'); - $company->setInn('1234567890123'); - } -} \ No newline at end of file diff --git a/tests/Unit/CompanyTest.php b/tests/Unit/CompanyTest.php deleted file mode 100644 index 4211303..0000000 --- a/tests/Unit/CompanyTest.php +++ /dev/null @@ -1,91 +0,0 @@ -checkAtolEntity($company); - $this->assertEquals(SnoTypes::OSN, $company->getSno()); - $this->assertEquals('5544332219', $company->getInn()); - $this->assertEquals('https://v4.online.atol.ru', $company->getPaymentAddress()); - $this->assertEquals('company@example.com', $company->getEmail()); - } - - /** - * Тестирует исключение о некорректной длине ИНН - * - * @throws \AtolOnline\Exceptions\AtolInnWrongLengthException - */ - public function testAtolInnWrongLengthException() - { - $company = new Company(); - $this->expectException(AtolInnWrongLengthException::class); - $company->setInn('321'); - $company->setInn('1234567890123'); - } - - /** - * Тестирует исключение о слишком длинном платёжном адресе - * - * @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException - */ - public function testAtolPaymentAddressTooLongException() - { - $company = new Company(); - $this->expectException(AtolPaymentAddressTooLongException::class); - $company->setPaymentAddress(self::randomString(257)); - } - - /** - * Тестирует исключение о слишком длинной почте - * - * @throws \AtolOnline\Exceptions\AtolEmailTooLongException - * @throws \AtolOnline\Exceptions\AtolEmailValidateException - */ - public function testAtolEmailTooLongException() - { - $company = new Company(); - $this->expectException(AtolEmailTooLongException::class); - $company->setEmail(self::randomString(65)); - } - - /** - * Тестирует исключение о некорректной почте - * - * @throws \AtolOnline\Exceptions\AtolEmailTooLongException - * @throws \AtolOnline\Exceptions\AtolEmailValidateException - */ - public function testAtolEmailValidateException() - { - $company = new Company(); - $this->expectException(AtolEmailValidateException::class); - $company->setEmail(self::randomString(15)); - } -} \ No newline at end of file diff --git a/tests/Unit/VatTest.php b/tests/VatTest_todo.php similarity index 61% rename from tests/Unit/VatTest.php rename to tests/VatTest_todo.php index 57f15d3..5810438 100644 --- a/tests/Unit/VatTest.php +++ b/tests/VatTest_todo.php @@ -1,39 +1,43 @@ assertEquals(0, $vat->getFinalSum(), 'Test '.$vat_type.' | 1 step'); + $this->assertEquals(0, $vat->getFinalSum(), 'Test ' . $vat_type . ' | 1 step'); $vat->setSum($sum); - $this->assertEquals($expected_set, $vat->getFinalSum(), 'Test '.$vat_type.' | 2 step'); + $this->assertEquals($expected_set, $vat->getFinalSum(), 'Test ' . $vat_type . ' | 2 step'); $vat->addSum(20); - $this->assertEquals($expected_add, $vat->getFinalSum(), 'Test '.$vat_type.' | 3 step'); + $this->assertEquals($expected_add, $vat->getFinalSum(), 'Test ' . $vat_type . ' | 3 step'); $vat->addSum(-20); } - + /** * Провайдер данных для тестирования разных типов ставок НДС * From 03591600dd25893d19684da739f2d26ce9a7a78f Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 18 Nov 2021 00:01:53 +0800 Subject: [PATCH 02/85] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=BC=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3=D0=B0=20(#8)=20=D0=B8=20=D1=80=D0=B5=D1=84?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - абстрактный класс AtolClient: - больше не наследуется от клиента guzzle, но содержит его объект - из Kkt вынесены методы, отвечающие за формирование запроса, отправку и получение ответа, в т.ч. авторизацию - переименованы исключения TooLongKktLoginException, TooLongKktPasswordException, EmptyKktLoginException и EmptyKktPasswordException - мелочи по AuthFailedException - заготовки тестов AtolClient и KktMonitor --- src/Api/AtolClient.php | 298 ++++++++++++++++++ src/Api/Kkt.php | 38 +-- src/Api/KktMonitor.php | 124 ++++++++ src/Api/KktResponse.php | 19 +- src/Exceptions/AuthFailedException.php | 16 +- ...nException.php => EmptyLoginException.php} | 2 +- ...ception.php => EmptyPasswordException.php} | 2 +- ...xception.php => TooLongLoginException.php} | 2 +- ...ption.php => TooLongPasswordException.php} | 2 +- tests/AtolClientTest.php | 48 +++ tests/KktMonitorTest.php | 40 +++ 11 files changed, 544 insertions(+), 47 deletions(-) create mode 100644 src/Api/AtolClient.php create mode 100644 src/Api/KktMonitor.php rename src/Exceptions/{EmptyKktLoginException.php => EmptyLoginException.php} (91%) rename src/Exceptions/{EmptyKktPasswordException.php => EmptyPasswordException.php} (91%) rename src/Exceptions/{TooLongKktLoginException.php => TooLongLoginException.php} (90%) rename src/Exceptions/{TooLongKktPasswordException.php => TooLongPasswordException.php} (90%) create mode 100644 tests/AtolClientTest.php create mode 100644 tests/KktMonitorTest.php diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php new file mode 100644 index 0000000..81e58f0 --- /dev/null +++ b/src/Api/AtolClient.php @@ -0,0 +1,298 @@ +http = new Client(array_merge($config, [ + 'http_errors' => $config['http_errors'] ?? false, + ])); + $login && $this->setLogin($login); + $password && $this->setPassword($password); + } + + /** + * Возвращает установленный флаг тестового режима + * + * @return bool + */ + public function isTestMode(): bool + { + return $this->test_mode; + } + + /** + * Устанавливает флаг тестового режима + * + * @param bool $test_mode + * @return $this + */ + public function setTestMode(bool $test_mode): 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): AtolClient + { + $this->token = $token; + return $this; + } + + /** + * Возвращает последний ответ сервера + * + * @return KktResponse|null + */ + public function getResponse(): ?KktResponse + { + return $this->response; + } + + /** + * Возвращает логин доступа к API + * + * @return string|null + */ + protected function getLogin(): ?string + { + return $this->login; + } + + /** + * Устанавливает логин доступа к API + * + * @param string $login + * @return $this + * @throws EmptyLoginException + * @throws TooLongLoginException + */ + public function setLogin(string $login): self + { + $login = trim($login); + if (empty($login)) { + throw new EmptyLoginException(); + } elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) { + throw new TooLongLoginException($login, Constraints::MAX_LENGTH_LOGIN); + } + $this->login = $login; + return $this; + } + + /** + * Возвращает пароль доступа к API + * + * @return string|null + */ + protected function getPassword(): ?string + { + return $this->password; + } + + /** + * Устанавливает пароль доступа к API + * + * @param string $password + * @return $this + * @throws EmptyPasswordException Пароль ККТ не может быть пустым + * @throws TooLongPasswordException Слишком длинный пароль ККТ + */ + public function setPassword(string $password): self + { + if (empty($password)) { + throw new EmptyPasswordException(); + } elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) { + throw new TooLongPasswordException($password, Constraints::MAX_LENGTH_PASSWORD); + } + $this->password = $password; + return $this; + } + + /** + * Возвращает набор заголовков для HTTP-запроса + * + * @return array + */ + private function getHeaders(): array + { + $headers['Content-type'] = 'application/json; charset=utf-8'; + if ($this->getToken()) { + $headers['Token'] = $this->getToken(); + } + return $headers; + } + + /** + * Возвращает полный URL для запроса + * + * @param string $method + * @return string + */ + protected function getUrlToMethod(string $method): string + { + return $this->getMainEndpoint() . '/' . trim($method); + } + + /** + * Отправляет авторизационный запрос на сервер АТОЛ и возвращает авторизационный токен + * + * @return string|null + * @throws AuthFailedException + * @throws EmptyPasswordException + * @throws EmptyLoginException + * @throws GuzzleException + */ + protected function doAuth(): ?string + { + $result = $this->sendRequest('POST', $this->getAuthEndpoint(), [ + 'login' => $this->getLogin() ?? throw new EmptyLoginException(), + 'pass' => $this->getPassword() ?? throw new EmptyPasswordException(), + ]); + if (!$result->isValid() || !$result->getContent()->token) { + throw new AuthFailedException($result); + } + return $result->getContent()?->token; + } + + /** + * Отправляет запрос и возвращает декодированный ответ + * + * @param string $http_method Метод HTTP + * @param string $url URL + * @param array|null $data Данные для передачи + * @param array|null $options Параметры Guzzle + * @return KktResponse + * @throws GuzzleException + * @see https://guzzle.readthedocs.io/en/latest/request-options.html + */ + protected function sendRequest( + string $http_method, + string $url, + ?array $data = null, + ?array $options = null + ): KktResponse { + $http_method = strtoupper(trim($http_method)); + $options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []); + if ($http_method != 'GET') { + $options['json'] = $data; + } + $response = $this->http->request($http_method, $url, $options); + return $this->response = new KktResponse($response); + } + + /** + * Выполняет авторизацию на сервере АТОЛ + * + * Авторизация выолнится только если неизвестен токен + * + * @param string|null $login + * @param string|null $password + * @return bool + * @throws AuthFailedException + * @throws TooLongLoginException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongPasswordException + * @throws GuzzleException + */ + abstract public function auth(?string $login = null, ?string $password = null): bool; + + /** + * Возвращает URL для запроса авторизации + * + * @return string + */ + abstract protected function getAuthEndpoint(): string; + + /** + * Возвращает URL для запросов + * + * @return string + */ + abstract protected function getMainEndpoint(): string; +} diff --git a/src/Api/Kkt.php b/src/Api/Kkt.php index 7459c74..89921ff 100644 --- a/src/Api/Kkt.php +++ b/src/Api/Kkt.php @@ -17,15 +17,15 @@ use AtolOnline\{ Entities\Document, Exceptions\AuthFailedException, Exceptions\EmptyCorrectionInfoException, - Exceptions\EmptyKktLoginException, - Exceptions\EmptyKktPasswordException, + Exceptions\EmptyLoginException, + Exceptions\EmptyPasswordException, Exceptions\InvalidCallbackUrlException, Exceptions\InvalidDocumentTypeException, Exceptions\InvalidInnLengthException, Exceptions\InvalidUuidException, Exceptions\TooLongCallbackUrlException, - Exceptions\TooLongKktLoginException, - Exceptions\TooLongKktPasswordException, + Exceptions\TooLongLoginException, + Exceptions\TooLongPasswordException, Exceptions\TooLongPaymentAddressException, Exceptions\TooManyItemsException, Exceptions\TooManyVatsException, @@ -70,10 +70,6 @@ class Kkt extends Client * @param string|null $pass * @param bool $test_mode Флаг тестового режима * @param array $guzzle_config Конфигурация GuzzleHttp - * @throws EmptyKktLoginException Логин ККТ не может быть пустым - * @throws TooLongKktLoginException Слишком длинный логин ККТ - * @throws EmptyKktPasswordException Пароль ККТ не может быть пустым - * @throws TooLongKktPasswordException Слишком длинный пароль ККТ * @see https://guzzle.readthedocs.io/en/latest/request-options.html */ public function __construct( @@ -126,15 +122,15 @@ class Kkt extends Client * * @param string $login * @return $this - * @throws EmptyKktLoginException Логин ККТ не может быть пустым - * @throws TooLongKktLoginException Слишком длинный логин ККТ + * @throws EmptyLoginException Логин ККТ не может быть пустым + * @throws TooLongLoginException Слишком длинный логин ККТ */ public function setLogin(string $login): Kkt { if (empty($login)) { - throw new EmptyKktLoginException(); + throw new EmptyLoginException(); } elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) { - throw new TooLongKktLoginException($login, Constraints::MAX_LENGTH_LOGIN); + throw new TooLongLoginException($login, Constraints::MAX_LENGTH_LOGIN); } $this->kkt_config['prod']['login'] = $login; return $this; @@ -155,15 +151,15 @@ class Kkt extends Client * * @param string $password * @return $this - * @throws EmptyKktPasswordException Пароль ККТ не может быть пустым - * @throws TooLongKktPasswordException Слишком длинный пароль ККТ + * @throws EmptyPasswordException Пароль ККТ не может быть пустым + * @throws TooLongPasswordException Слишком длинный пароль ККТ */ public function setPassword(string $password): Kkt { if (empty($password)) { - throw new EmptyKktPasswordException(); + throw new EmptyPasswordException(); } elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) { - throw new TooLongKktPasswordException($password, Constraints::MAX_LENGTH_PASSWORD); + throw new TooLongPasswordException($password, Constraints::MAX_LENGTH_PASSWORD); } $this->kkt_config['prod']['pass'] = $password; return $this; @@ -207,16 +203,6 @@ class Kkt extends Client { return $this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['callback_url']; } - - /** - * Возвращает последний ответ сервера - * - * @return KktResponse|null - */ - public function getLastResponse(): ?KktResponse - { - return $this->last_response; - } /** * Возвращает флаг тестового режима diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php new file mode 100644 index 0000000..8ea7037 --- /dev/null +++ b/src/Api/KktMonitor.php @@ -0,0 +1,124 @@ +isTestMode() + ? 'https://testonline.atol.ru/api/auth/v1/gettoken' + : 'https://online.atol.ru/api/auth/v1/gettoken'; + } + + /** + * @inheritDoc + */ + protected function getMainEndpoint(): string + { + return $this->isTestMode() + ? 'https://testonline.atol.ru/api/kkt/v1' + : 'https://online.atol.ru/api/kkt/v1'; + } + + /** + * @inheritDoc + */ + public function auth(?string $login = null, ?string $password = null): bool + { + if (empty($this->getToken())) { + $login && $this->setLogin($login); + $password && $this->setPassword($password); + if ($token = $this->doAuth()) { + $this->setToken($token); + } + } + return !empty($this->getToken()); + } + + /** + * Получает от API информацию обо всех ККТ и ФН в рамках группы + * + * @param int|null $limit + * @param int|null $offset + * @return KktResponse + * @throws GuzzleException + * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9 + */ + protected function fetchAll(?int $limit = null, ?int $offset = null): KktResponse + { + $params = []; + $limit && $params['limit'] = $limit; + $offset && $params['offset'] = $offset; + return $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params); + } + + /** + * Возвращает информацию обо всех ККТ и ФН в рамках группы + * + * @param int|null $limit + * @param int|null $offset + * @return KktResponse + * @throws GuzzleException + * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9 + */ + public function getAll(?int $limit = null, ?int $offset = null): KktResponse + { + return $this->fetchAll($limit, $offset); + } + + /** + * Получает от API информацию о конкретной ККТ по её серийному номеру + * + * @param string $serial_number + * @return KktResponse + * @throws GuzzleException + * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11 + */ + protected function fetchOne(string $serial_number): KktResponse + { + return $this->sendRequest( + 'GET', + self::getUrlToMethod('cash-registers') . '/' . trim($serial_number), + options: [ + 'headers' => [ + 'Accept' => 'application/hal+json', + ], + ] + ); + } + + /** + * Возвращает информацию о конкретной ККТ по её серийному номеру + * + * @todo кастовать к отдельному классу со своими геттерами + * @param string $serial_number + * @return stdClass + * @throws GuzzleException + * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11 + */ + public function getOne(string $serial_number): stdClass + { + return $this->fetchOne($serial_number)->getContent()->data; + } +} diff --git a/src/Api/KktResponse.php b/src/Api/KktResponse.php index 7fe359a..796df1a 100644 --- a/src/Api/KktResponse.php +++ b/src/Api/KktResponse.php @@ -14,6 +14,7 @@ namespace AtolOnline\Api; use JsonSerializable; use Psr\Http\Message\ResponseInterface; use stdClass; +use Stringable; /** * Класс AtolResponse, описывающий ответ от ККТ @@ -21,7 +22,7 @@ use stdClass; * @property mixed $error * @package AtolOnline\Api */ -class KktResponse implements JsonSerializable +class KktResponse implements JsonSerializable, Stringable { /** * @var int Код ответа сервера @@ -29,9 +30,9 @@ class KktResponse implements JsonSerializable protected int $code; /** - * @var stdClass Содержимое ответа сервера + * @var stdClass|array|null Содержимое ответа сервера */ - protected $content; + protected stdClass|array|null $content; /** * @var array Заголовки ответа @@ -66,9 +67,9 @@ class KktResponse implements JsonSerializable * @param $name * @return mixed */ - public function __get($name) + public function __get($name): mixed { - return $this->getContent()->$name; + return $this->getContent()?->$name; } /** @@ -84,9 +85,9 @@ class KktResponse implements JsonSerializable /** * Возвращает объект результата запроса * - * @return stdClass|null + * @return mixed */ - public function getContent(): ?stdClass + public function getContent(): mixed { return $this->content; } @@ -107,7 +108,7 @@ class KktResponse implements JsonSerializable /** * Возвращает текстовое представление */ - public function __toString() + public function __toString(): string { return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE); } @@ -115,7 +116,7 @@ class KktResponse implements JsonSerializable /** * @inheritDoc */ - public function jsonSerialize() + public function jsonSerialize(): array { return [ 'code' => $this->code, diff --git a/src/Exceptions/AuthFailedException.php b/src/Exceptions/AuthFailedException.php index 9c2c76a..e213f6b 100644 --- a/src/Exceptions/AuthFailedException.php +++ b/src/Exceptions/AuthFailedException.php @@ -16,26 +16,26 @@ use Exception; use Throwable; /** - * Исключение, возникающее при работе с АТОЛ Онлайн + * Исключение, возникающее при неудачной авторизации */ class AuthFailedException extends Exception { /** * Конструктор * - * @param KktResponse $last_response + * @param KktResponse $response * @param string $message * @param int $code * @param Throwable|null $previous */ - public function __construct(KktResponse $last_response, $message = "", $code = 0, Throwable $previous = null) + public function __construct(KktResponse $response, $message = "", $code = 0, Throwable $previous = null) { - $message = $last_response->isValid() + $message = $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; + : '[' . $response->error->code . '] ' . $response->error->text . + '. ERROR_ID: ' . $response->error->error_id . + '. TYPE: ' . $response->error->type; + $code = $response->isValid() ? $code : $response->error->code; parent::__construct($message, $code, $previous); } } diff --git a/src/Exceptions/EmptyKktLoginException.php b/src/Exceptions/EmptyLoginException.php similarity index 91% rename from src/Exceptions/EmptyKktLoginException.php rename to src/Exceptions/EmptyLoginException.php index 112b799..fbd4b5e 100644 --- a/src/Exceptions/EmptyKktLoginException.php +++ b/src/Exceptions/EmptyLoginException.php @@ -14,7 +14,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать пустой логин ККТ */ -class EmptyKktLoginException extends AtolException +class EmptyLoginException extends AtolException { /** * @var string Сообщение об ошибке diff --git a/src/Exceptions/EmptyKktPasswordException.php b/src/Exceptions/EmptyPasswordException.php similarity index 91% rename from src/Exceptions/EmptyKktPasswordException.php rename to src/Exceptions/EmptyPasswordException.php index aba4aa9..d3370b8 100644 --- a/src/Exceptions/EmptyKktPasswordException.php +++ b/src/Exceptions/EmptyPasswordException.php @@ -14,7 +14,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать пустой пароль ККТ */ -class EmptyKktPasswordException extends AtolException +class EmptyPasswordException extends AtolException { /** * @var string Сообщение об ошибке diff --git a/src/Exceptions/TooLongKktLoginException.php b/src/Exceptions/TooLongLoginException.php similarity index 90% rename from src/Exceptions/TooLongKktLoginException.php rename to src/Exceptions/TooLongLoginException.php index 7d35711..51170e9 100644 --- a/src/Exceptions/TooLongKktLoginException.php +++ b/src/Exceptions/TooLongLoginException.php @@ -14,7 +14,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать слишком длинный логин ККТ */ -class TooLongKktLoginException extends BasicTooLongException +class TooLongLoginException extends BasicTooLongException { /** * @var string Сообщение об ошибке diff --git a/src/Exceptions/TooLongKktPasswordException.php b/src/Exceptions/TooLongPasswordException.php similarity index 90% rename from src/Exceptions/TooLongKktPasswordException.php rename to src/Exceptions/TooLongPasswordException.php index 756767f..c9fb51e 100644 --- a/src/Exceptions/TooLongKktPasswordException.php +++ b/src/Exceptions/TooLongPasswordException.php @@ -14,7 +14,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать слишком длинный пароль ККТ */ -class TooLongKktPasswordException extends BasicTooLongException +class TooLongPasswordException extends BasicTooLongException { /** * @var string Сообщение об ошибке diff --git a/tests/AtolClientTest.php b/tests/AtolClientTest.php new file mode 100644 index 0000000..b5f387d --- /dev/null +++ b/tests/AtolClientTest.php @@ -0,0 +1,48 @@ + Date: Thu, 18 Nov 2021 19:07:32 +0800 Subject: [PATCH 03/85] =?UTF-8?q?[WIP]=20=D0=9D=D0=B0=D1=87=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B4=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=BC=20KktMonitorTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- src/Api/AtolClient.php | 15 ++-- tests/AtolClientTest.php | 48 ------------ tests/BasicTestCase.php | 118 +++++++++++++++++++++++++++-- tests/ClientTest.php | 5 +- tests/CompanyTest.php | 2 +- tests/HelpersTest.php | 2 +- tests/ItemTest_todo.php | 2 +- tests/KktMonitorTest.php | 159 ++++++++++++++++++++++++++++++++++----- tests/VatTest_todo.php | 5 +- 10 files changed, 270 insertions(+), 88 deletions(-) delete mode 100644 tests/AtolClientTest.php diff --git a/composer.json b/composer.json index c9438f6..be3ec00 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ }, "autoload-dev": { "psr-4": { - "AtolOnline\\Tests\\": "tests/" + "AtolOnlineTests\\": "tests/" } }, "scripts": { diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index 81e58f0..213e790 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -29,7 +29,7 @@ abstract class AtolClient /** * @var bool Флаг тестового режима */ - protected bool $test_mode = true; + protected bool $test_mode; /** * @var Client HTTP-клиент для работы с API @@ -59,6 +59,7 @@ abstract class AtolClient /** * Конструктор * + * @param bool $test_mode * @param string|null $login * @param string|null $password * @param array $config @@ -69,6 +70,7 @@ abstract class AtolClient * @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 = [] @@ -76,8 +78,9 @@ abstract class AtolClient $this->http = new Client(array_merge($config, [ 'http_errors' => $config['http_errors'] ?? false, ])); - $login && $this->setLogin($login); - $password && $this->setPassword($password); + $this->setTestMode($test_mode); + !is_null($login) && $this->setLogin($login); + !is_null($password) && $this->setPassword($password); } /** @@ -96,7 +99,7 @@ abstract class AtolClient * @param bool $test_mode * @return $this */ - public function setTestMode(bool $test_mode): self + public function setTestMode(bool $test_mode = true): self { $this->test_mode = $test_mode; return $this; @@ -139,7 +142,7 @@ abstract class AtolClient * * @return string|null */ - protected function getLogin(): ?string + public function getLogin(): ?string { return $this->login; } @@ -169,7 +172,7 @@ abstract class AtolClient * * @return string|null */ - protected function getPassword(): ?string + public function getPassword(): ?string { return $this->password; } diff --git a/tests/AtolClientTest.php b/tests/AtolClientTest.php deleted file mode 100644 index b5f387d..0000000 --- a/tests/AtolClientTest.php +++ /dev/null @@ -1,48 +0,0 @@ - false]))->request('GET', $url); + //$this->assertEquals(200, $result->getStatusCode()); + return $result->getStatusCode() === $code; + } + + /** + * Проверяет доступность API мониторинга + * + * @return bool + * @throws GuzzleException + */ + protected function isMonitoringOnline(): bool + { + return $this->ping('https://testonline.atol.ru/api/auth/v1/gettoken', 400); + } + + /** + * Пропускает текущий тест если API мониторинга недоступно + * + * @throws GuzzleException + */ + protected function skipIfMonitoringIsOffline(): void + { + if (!$this->isMonitoringOnline()) { + $this->markTestSkipped($this->getName() . ': Monitoring API is inaccessible. Skipping test.'); + } } /** @@ -50,6 +79,79 @@ class BasicTestCase extends TestCase } } + /** + * Тестирует идентичность двух классов + * + * @param object|string $expected + * @param object|string $actual + */ + public function assertIsSameClass(object|string $expected, object|string $actual) + { + $this->assertEquals( + is_object($expected) ? $expected::class : $expected, + is_object($actual) ? $actual::class : $actual + ); + } + + /** + * Тестирует наследование класса (объекта) от указанных классов + * + * @param string[] $parents + * @param object|string $actual + */ + public function assertExtendsClasses(array $parents, object|string $actual) + { + $this->checkClassesIntersection($parents, $actual, 'class_parents'); + } + + /** + * Тестирует имплементацию классом (объектом) указанных интерфейсов + * + * @param string[] $parents + * @param object|string $actual + */ + public function assertImplementsInterfaces(array $parents, object|string $actual) + { + $this->checkClassesIntersection($parents, $actual, 'class_implements'); + } + + /** + * Тестирует использование классом (объектом) указанных трейтов + * + * @param string[] $parents + * @param object|string $actual + */ + public function assertUsesTraits(array $parents, object|string $actual) + { + $this->checkClassesIntersection($parents, $actual, 'class_uses'); + } + + /** + * Проверяет пересечение классов указанной функцией SPL + * + * @param object|string $class Класс для проверки на вхождение, или объект, класс коего нужно проверить + * @param array $classes Массив классов, вхождение в который нужно проверить + * @param string $function class_parents|class_implements|class_uses + */ + protected function checkClassesIntersection(array $classes, object|string $class, string $function): void + { + $actual_classes = is_object($class) ? $function($class) : [$class::class]; + $this->assertIsArray($actual_classes); + $this->assertNotEmpty(array_intersect($classes, $actual_classes)); + } + + /** + * Тестирует, является ли объект коллекцией + * + * @param mixed $expected + */ + public function assertIsCollection(mixed $expected): void + { + $this->assertIsObject($expected); + $this->assertIsIterable($expected); + $this->assertIsSameClass($expected, Collection::class); + } + /** * Провайдер валидных телефонов * diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 8447c27..be2ce7a 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests; +namespace AtolOnlineTests; use AtolOnline\{ Entities\Client, @@ -16,7 +16,8 @@ use AtolOnline\{ Exceptions\TooLongEmailException, Exceptions\TooLongNameException, Exceptions\TooLongPhoneException, - Helpers}; + Helpers +}; /** * Набор тестов для проверки работы класс покупателя diff --git a/tests/CompanyTest.php b/tests/CompanyTest.php index cad4c86..7b8e20c 100644 --- a/tests/CompanyTest.php +++ b/tests/CompanyTest.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests; +namespace AtolOnlineTests; use AtolOnline\{ Constants\SnoTypes, diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php index 1259886..7715516 100644 --- a/tests/HelpersTest.php +++ b/tests/HelpersTest.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests; +namespace AtolOnlineTests; use AtolOnline\Helpers; diff --git a/tests/ItemTest_todo.php b/tests/ItemTest_todo.php index 59a8288..88a4088 100644 --- a/tests/ItemTest_todo.php +++ b/tests/ItemTest_todo.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests; +namespace AtolOnlineTests; use AtolOnline\{ Constants\PaymentMethods, diff --git a/tests/KktMonitorTest.php b/tests/KktMonitorTest.php index 4d5fe7c..fbedd50 100644 --- a/tests/KktMonitorTest.php +++ b/tests/KktMonitorTest.php @@ -1,40 +1,163 @@ assertIsObject($client); + $this->assertIsSameClass(KktMonitor::class, $client); + $this->assertExtendsClasses([AtolClient::class], $client); + $this->assertNull($client->getLogin()); + $this->assertNull($client->getPassword()); + } - public function testSetToken() + /** + * Тестирует успешное создание объекта монитора с аргументами конструктора + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::setLogin + * @covers \AtolOnline\Api\KktMonitor::setPassword + * @covers \AtolOnline\Api\KktMonitor::getLogin + * @covers \AtolOnline\Api\KktMonitor::getPassword + */ + public function testConstructorWithArgs() + { + $client = new KktMonitor(false, 'login', 'password', []); + $this->assertIsObject($client); + $this->assertIsSameClass(KktMonitor::class, $client); + $this->assertExtendsClasses([AtolClient::class], $client); + //$this->assertFalse($client->isTestMode()); + //$this->assertEquals('login', $client->getLogin()); + //$this->assertEquals('password', $client->getPassword()); + } + + /** + * Тестирует исключение при попытке передать пустой логин в конструктор + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::setLogin + */ + public function testConstructorWithShortLogin() + { + $this->expectException(EmptyLoginException::class); + new KktMonitor(login: ''); + } + + /** + * Тестирует исключение при попытке передать слишком длинный логин в конструктор + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::setLogin + */ + public function testConstructorWithLongLogin() + { + $this->expectException(TooLongLoginException::class); + new KktMonitor(login: Helpers::randomStr(101)); + } + + /** + * Тестирует исключение при попытке передать пустой пароль в конструктор + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::setPassword + */ + public function testConstructorWithShortPassword() + { + $this->expectException(EmptyPasswordException::class); + new KktMonitor(password: ''); + } + + /** + * Тестирует исключение при попытке передать слишком длинный пароль в конструктор + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::setPassword + */ + public function testConstructorWithLongPassword() + { + $this->expectException(TooLongPasswordException::class); + new KktMonitor(password: Helpers::randomStr(101)); + } + + /** + * Тестирует установку тестового режима + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::isTestMode + * @covers \AtolOnline\Api\KktMonitor::setTestMode + */ + public function testTestMode() + { + $client = new KktMonitor(); + $this->assertTrue($client->isTestMode()); + + $client = new KktMonitor(true); + $this->assertTrue($client->isTestMode()); + + $client = new KktMonitor(false); + $this->assertFalse($client->isTestMode()); + + $client = (new KktMonitor())->setTestMode(); + $this->assertTrue($client->isTestMode()); + + $client = (new KktMonitor())->setTestMode(true); + $this->assertTrue($client->isTestMode()); + + $client = (new KktMonitor())->setTestMode(false); + $this->assertFalse($client->isTestMode()); + } + + public function todo_testGetToken() { } - public function testGetResponse() + public function todo_testGetResponse() + { + //$this->skipIfMonitoringIsOffline(); + } + + public function todo_testSetPassword() { } - public function testSetLogin() + public function todo_testAuth() { } - public function testAuth() + public function todo_testGetAll() { } - public function testGetToken() + public function todo_testSetToken() { } - public function testSetPassword() + public function todo_testGetOne() + { + } + + public function todo_testSetLogin() { } } diff --git a/tests/VatTest_todo.php b/tests/VatTest_todo.php index 5810438..b65fee9 100644 --- a/tests/VatTest_todo.php +++ b/tests/VatTest_todo.php @@ -7,11 +7,12 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests; +namespace AtolOnlineTests; use AtolOnline\{ Constants\VatTypes, - Entities\Vat}; + Entities\Vat +}; /** * Class VatTest From 92a2c6cc48acb09d7d9e5e59112f9dd8f4fae13b Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 19 Nov 2021 13:42:51 +0800 Subject: [PATCH 04/85] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=D1=8B=20=D1=81=D1=83=D1=89=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89=D0=B8=D0=B5=20=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D1=8B=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=20AtolClient?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Используется тестовый API АТОЛ Онлайн. 94% покрытие AtolClient, но ещё не закончено --- tests/KktMonitorTest.php | 142 ++++++++++++++++++++++++++++++++------- 1 file changed, 119 insertions(+), 23 deletions(-) diff --git a/tests/KktMonitorTest.php b/tests/KktMonitorTest.php index fbedd50..e2e4b1c 100644 --- a/tests/KktMonitorTest.php +++ b/tests/KktMonitorTest.php @@ -4,11 +4,15 @@ namespace AtolOnlineTests; use AtolOnline\Api\AtolClient; use AtolOnline\Api\KktMonitor; +use AtolOnline\Api\KktResponse; +use AtolOnline\Exceptions\AuthFailedException; use AtolOnline\Exceptions\EmptyLoginException; use AtolOnline\Exceptions\EmptyPasswordException; use AtolOnline\Exceptions\TooLongLoginException; use AtolOnline\Exceptions\TooLongPasswordException; use AtolOnline\Helpers; +use AtolOnline\TestEnvParams; +use GuzzleHttp\Exception\GuzzleException; /** * Набор тестов для проверки работы API-клиента на примере монитора ККТ @@ -19,8 +23,6 @@ class KktMonitorTest extends BasicTestCase * Тестирует успешное создание объекта монитора без аргументов конструктора * * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::getLogin - * @covers \AtolOnline\Api\KktMonitor::getPassword */ public function testConstructorWithoutArgs() { @@ -28,8 +30,6 @@ class KktMonitorTest extends BasicTestCase $this->assertIsObject($client); $this->assertIsSameClass(KktMonitor::class, $client); $this->assertExtendsClasses([AtolClient::class], $client); - $this->assertNull($client->getLogin()); - $this->assertNull($client->getPassword()); } /** @@ -37,8 +37,8 @@ class KktMonitorTest extends BasicTestCase * * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::setLogin - * @covers \AtolOnline\Api\KktMonitor::setPassword * @covers \AtolOnline\Api\KktMonitor::getLogin + * @covers \AtolOnline\Api\KktMonitor::setPassword * @covers \AtolOnline\Api\KktMonitor::getPassword */ public function testConstructorWithArgs() @@ -47,9 +47,25 @@ class KktMonitorTest extends BasicTestCase $this->assertIsObject($client); $this->assertIsSameClass(KktMonitor::class, $client); $this->assertExtendsClasses([AtolClient::class], $client); - //$this->assertFalse($client->isTestMode()); - //$this->assertEquals('login', $client->getLogin()); - //$this->assertEquals('password', $client->getPassword()); + } + + /** + * Тестирует установку и возврат логина + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::getLogin + * @covers \AtolOnline\Api\KktMonitor::setLogin + */ + public function testLogin() + { + $client = new KktMonitor(login: 'login'); + $this->assertEquals('login', $client->getLogin()); + + $client = new KktMonitor(); + $this->assertNull($client->getLogin()); + + $client->setLogin('login'); + $this->assertEquals('login', $client->getLogin()); } /** @@ -57,8 +73,9 @@ class KktMonitorTest extends BasicTestCase * * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::setLogin + * @covers \AtolOnline\Exceptions\EmptyLoginException */ - public function testConstructorWithShortLogin() + public function testEmptyLoginException() { $this->expectException(EmptyLoginException::class); new KktMonitor(login: ''); @@ -69,20 +86,41 @@ class KktMonitorTest extends BasicTestCase * * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::setLogin + * @covers \AtolOnline\Exceptions\TooLongLoginException */ - public function testConstructorWithLongLogin() + public function testTooLongLoginException() { $this->expectException(TooLongLoginException::class); new KktMonitor(login: Helpers::randomStr(101)); } + /** + * Тестирует установку и возврат пароля + * + * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\KktMonitor::getPassword + * @covers \AtolOnline\Api\KktMonitor::setPassword + */ + public function testPassword() + { + $client = new KktMonitor(password: 'password'); + $this->assertEquals('password', $client->getPassword()); + + $client = new KktMonitor(); + $this->assertNull($client->getPassword()); + + $client->setPassword('password'); + $this->assertEquals('password', $client->getPassword()); + } + /** * Тестирует исключение при попытке передать пустой пароль в конструктор * * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::setPassword + * @covers \AtolOnline\Exceptions\EmptyPasswordException */ - public function testConstructorWithShortPassword() + public function testEmptyPasswordException() { $this->expectException(EmptyPasswordException::class); new KktMonitor(password: ''); @@ -128,36 +166,94 @@ class KktMonitorTest extends BasicTestCase $this->assertFalse($client->isTestMode()); } - public function todo_testGetToken() + /** + * Возвращает объект клиента для тестирования с тестовым API АТОЛ + * + * @return AtolClient + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + private function newTestClient(): AtolClient { + $credentials = TestEnvParams::FFD105(); + return (new KktMonitor(true)) + ->setLogin($credentials['login']) + ->setPassword($credentials['password']); } - public function todo_testGetResponse() + /** + * Тестирует авторизацию + * + * @covers \AtolOnline\Api\AtolClient::getHeaders + * @covers \AtolOnline\Api\KktMonitor::sendRequest + * @covers \AtolOnline\Api\KktMonitor::getAuthEndpoint + * @covers \AtolOnline\Api\KktMonitor::doAuth + * @covers \AtolOnline\Api\KktMonitor::auth + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws AuthFailedException + * @throws GuzzleException + */ + public function testAuth() { - //$this->skipIfMonitoringIsOffline(); + $this->skipIfMonitoringIsOffline(); + $result = $this->newTestClient()->auth(); + $this->assertTrue($result); } - public function todo_testSetPassword() + /** + * Тестирует возврат токена после авторизации + * + * @covers \AtolOnline\Api\KktMonitor::setToken + * @covers \AtolOnline\Api\KktMonitor::getToken + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function testGetToken() { + $client = new KktMonitor(); + $this->assertNull($client->getToken()); + + $this->skipIfMonitoringIsOffline(); + $client = $this->newTestClient(); + $client->auth(); + $this->assertIsString($client->getToken()); } - public function todo_testAuth() + /** + * Тестирует возврат объекта последнего ответа от API + * + * @covers \AtolOnline\Api\KktMonitor::getResponse + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function testGetResponse() { + $this->skipIfMonitoringIsOffline(); + $client = $this->newTestClient(); + $client->auth(); + $this->assertIsSameClass(KktResponse::class, $client->getResponse()); } + public function todo_testGetAll() { } - public function todo_testSetToken() - { - } - public function todo_testGetOne() { } - public function todo_testSetLogin() - { - } } From 2c5144caacd84ccb378b463f3d39bfc9e2aca3d9 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 19 Nov 2021 18:29:09 +0800 Subject: [PATCH 05/85] =?UTF-8?q?KktMonitor::getAll()=20=D1=82=D0=B5=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D1=8C=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0=D1=89?= =?UTF-8?q?=D0=B0=D0=B5=D1=82=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D1=8E=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=9A=D0=9A=D0=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit При этом остаётся возможность получить полный ответ через KktMonitor::getResponse() --- src/Api/KktMonitor.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index 8ea7037..d61ddbe 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace AtolOnline\Api; use GuzzleHttp\Exception\GuzzleException; +use Illuminate\Support\Collection; use stdClass; /** @@ -78,13 +79,13 @@ class KktMonitor extends AtolClient * * @param int|null $limit * @param int|null $offset - * @return KktResponse + * @return Collection * @throws GuzzleException * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9 */ - public function getAll(?int $limit = null, ?int $offset = null): KktResponse + public function getAll(?int $limit = null, ?int $offset = null): Collection { - return $this->fetchAll($limit, $offset); + return collect($this->fetchAll($limit, $offset)->getContent()); } /** From 6551366d84037784c0341d154be71abda143775a Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 19 Nov 2021 18:42:14 +0800 Subject: [PATCH 06/85] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B5=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=20AtolClient=20+=20KktMonitor=20+=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=D0=B8=D0=BD=D1=8B=20=D0=B8=D1=81=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Часть тестов завязаны на тестовый API мониторинга Атола. Иногда он закашливается и не отвечает, возможно, там рейтлимит. Да и пофиг, моки -- злейшее зло, и мне лень их писать. --- tests/BasicTestCase.php | 11 ++++-- tests/KktMonitorTest.php | 79 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/tests/BasicTestCase.php b/tests/BasicTestCase.php index e25adcd..f20ff62 100644 --- a/tests/BasicTestCase.php +++ b/tests/BasicTestCase.php @@ -28,12 +28,17 @@ class BasicTestCase extends TestCase * @param string $url * @param int $code * @return bool - * @throws GuzzleException */ protected function ping(string $url, int $code): bool { - $result = (new Client(['http_errors' => false]))->request('GET', $url); - //$this->assertEquals(200, $result->getStatusCode()); + try { + $result = (new Client([ + 'http_errors' => false, + 'timeout' => 3, + ]))->request('GET', $url); + } catch (GuzzleException) { + return false; + } return $result->getStatusCode() === $code; } diff --git a/tests/KktMonitorTest.php b/tests/KktMonitorTest.php index e2e4b1c..a6ebde6 100644 --- a/tests/KktMonitorTest.php +++ b/tests/KktMonitorTest.php @@ -40,6 +40,10 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::getLogin * @covers \AtolOnline\Api\KktMonitor::setPassword * @covers \AtolOnline\Api\KktMonitor::getPassword + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongLoginException + * @throws TooLongPasswordException */ public function testConstructorWithArgs() { @@ -55,6 +59,8 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::getLogin * @covers \AtolOnline\Api\KktMonitor::setLogin + * @throws EmptyLoginException + * @throws TooLongLoginException */ public function testLogin() { @@ -87,6 +93,10 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::setLogin * @covers \AtolOnline\Exceptions\TooLongLoginException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongLoginException + * @throws TooLongPasswordException */ public function testTooLongLoginException() { @@ -100,6 +110,8 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::getPassword * @covers \AtolOnline\Api\KktMonitor::setPassword + * @throws EmptyPasswordException + * @throws TooLongPasswordException */ public function testPassword() { @@ -131,6 +143,10 @@ class KktMonitorTest extends BasicTestCase * * @covers \AtolOnline\Api\KktMonitor::__construct * @covers \AtolOnline\Api\KktMonitor::setPassword + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongLoginException + * @throws TooLongPasswordException */ public function testConstructorWithLongPassword() { @@ -175,7 +191,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - private function newTestClient(): AtolClient + private function newTestClient(): KktMonitor { $credentials = TestEnvParams::FFD105(); return (new KktMonitor(true)) @@ -208,8 +224,9 @@ class KktMonitorTest extends BasicTestCase /** * Тестирует возврат токена после авторизации * - * @covers \AtolOnline\Api\KktMonitor::setToken - * @covers \AtolOnline\Api\KktMonitor::getToken + * @depends testAuth + * @covers \AtolOnline\Api\KktMonitor::setToken + * @covers \AtolOnline\Api\KktMonitor::getToken * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -231,7 +248,8 @@ class KktMonitorTest extends BasicTestCase /** * Тестирует возврат объекта последнего ответа от API * - * @covers \AtolOnline\Api\KktMonitor::getResponse + * @depends testAuth + * @covers \AtolOnline\Api\KktMonitor::getResponse * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -247,13 +265,58 @@ class KktMonitorTest extends BasicTestCase $this->assertIsSameClass(KktResponse::class, $client->getResponse()); } - - public function todo_testGetAll() + /** + * [Мониторинг] Тестирует получение данных о всех ККТ + * + * @depends testAuth + * @covers \AtolOnline\Api\KktMonitor::getMainEndpoint + * @covers \AtolOnline\Api\AtolClient::getUrlToMethod + * @covers \AtolOnline\Api\KktMonitor::fetchAll + * @covers \AtolOnline\Api\KktMonitor::getAll + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function testMonitorGetAll() { + $this->skipIfMonitoringIsOffline(); + $client = $this->newTestClient(); + $client->auth(); + $kkts = $client->getAll(); + $this->assertNotEmpty($client->getResponse()->getContent()); + $this->assertIsCollection($kkts); + $this->assertTrue($kkts->count() > 0); } - public function todo_testGetOne() + /** + * [Мониторинг] Тестирует получение данных о конкретной ККТ + * + * @depends testAuth + * @covers \AtolOnline\Api\KktMonitor::getMainEndpoint + * @covers \AtolOnline\Api\AtolClient::getUrlToMethod + * @covers \AtolOnline\Api\KktMonitor::fetchOne + * @covers \AtolOnline\Api\KktMonitor::getOne + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function testGetOne() { + $this->skipIfMonitoringIsOffline(); + $client = $this->newTestClient(); + $client->auth(); + $kkts = $client->getAll(); + $serial_number = $kkts->first()->serialNumber; + $client->getOne($serial_number); + $this->assertIsSameClass(KktResponse::class, $client->getResponse()); + $this->assertNotEmpty($client->getResponse()); + $this->assertNotNull($client->getResponse()->data->serialNumber); + $this->assertEquals($serial_number, $client->getResponse()->data->serialNumber); } - } From e1120051c19550d3597911701588e3f1763ebe0d Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 20 Nov 2021 23:38:19 +0800 Subject: [PATCH 07/85] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D0=B0=D1=8F=20=D1=81?= =?UTF-8?q?=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20Kkt=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D0=BE=D0=B2=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=D0=B0=20?= =?UTF-8?q?(#8)=20+=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=B8=D0=B9=20=D1=80=D0=B5?= =?UTF-8?q?=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - enum-константы перенесены в своё пространство Enums - новые исключения EmptyMonitorDataException + NotEnoughMonitorDataException - KktMonitor::getAll() теперь возвращает коллекцию объектов Kkt - KktMonitor::getOne() теперь возвращает объект Kkt - местами актуализированы return types + phpdoc Покрытие тестами: - 61% исключений - 98% AtolClient (пока хз как покрыть 208-ую строку) - 100% KktMonitor - 100% Kkt - 100% Client - 100% Company - 100% Entity --- src/Api/KktMonitor.php | 15 +- src/Entities/Company.php | 2 +- src/Entities/Entity.php | 2 +- src/Entities/Kkt.php | 149 +++++++++++++++++ src/Entities/Payment.php | 2 +- src/Entities/Vat.php | 2 +- src/{Constants => Enums}/CorrectionTypes.php | 2 +- src/{Constants => Enums}/DocumentTypes.php | 2 +- src/{Constants => Enums}/PaymentMethods.php | 2 +- src/{Constants => Enums}/PaymentObjects.php | 2 +- src/{Constants => Enums}/PaymentTypes.php | 2 +- .../ReceiptOperationTypes.php | 2 +- src/{Constants => Enums}/SnoTypes.php | 2 +- src/{Constants => Enums}/VatTypes.php | 2 +- src/Exceptions/EmptyMonitorDataException.php | 23 +++ .../InvalidDocumentTypeException.php | 2 +- src/Exceptions/InvalidSnoException.php | 2 +- .../NotEnoughMonitorDataException.php | 38 +++++ tests/BasicTestCase.php | 13 +- tests/CompanyTest.php | 2 +- tests/ItemTest_todo.php | 6 +- tests/KktEntityTest.php | 153 ++++++++++++++++++ tests/KktMonitorTest.php | 88 +++++----- tests/VatTest_todo.php | 5 +- 24 files changed, 449 insertions(+), 71 deletions(-) create mode 100644 src/Entities/Kkt.php rename src/{Constants => Enums}/CorrectionTypes.php (96%) rename src/{Constants => Enums}/DocumentTypes.php (95%) rename src/{Constants => Enums}/PaymentMethods.php (98%) rename src/{Constants => Enums}/PaymentObjects.php (99%) rename src/{Constants => Enums}/PaymentTypes.php (98%) rename src/{Constants => Enums}/ReceiptOperationTypes.php (97%) rename src/{Constants => Enums}/SnoTypes.php (97%) rename src/{Constants => Enums}/VatTypes.php (96%) create mode 100644 src/Exceptions/EmptyMonitorDataException.php create mode 100644 src/Exceptions/NotEnoughMonitorDataException.php create mode 100644 tests/KktEntityTest.php diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index d61ddbe..f1fb41e 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -11,9 +11,11 @@ declare(strict_types = 1); namespace AtolOnline\Api; +use AtolOnline\Entities\Kkt; +use AtolOnline\Exceptions\EmptyMonitorDataException; +use AtolOnline\Exceptions\NotEnoughMonitorDataException; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Collection; -use stdClass; /** * Класс для мониторинга ККТ @@ -85,7 +87,8 @@ class KktMonitor extends AtolClient */ public function getAll(?int $limit = null, ?int $offset = null): Collection { - return collect($this->fetchAll($limit, $offset)->getContent()); + $collection = collect($this->fetchAll($limit, $offset)->getContent()); + return $collection->map(fn($data) => new Kkt($data)); } /** @@ -114,12 +117,14 @@ class KktMonitor extends AtolClient * * @todo кастовать к отдельному классу со своими геттерами * @param string $serial_number - * @return stdClass + * @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): stdClass + public function getOne(string $serial_number): Kkt { - return $this->fetchOne($serial_number)->getContent()->data; + return new Kkt($this->fetchOne($serial_number)->getContent()->data); } } diff --git a/src/Entities/Company.php b/src/Entities/Company.php index f37f955..d5876d9 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -13,7 +13,7 @@ namespace AtolOnline\Entities; use AtolOnline\{ Constants\Constraints, - Constants\SnoTypes, + Enums\SnoTypes, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, Exceptions\InvalidPaymentAddressException, diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index 0b47b2d..490fdc8 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -28,4 +28,4 @@ abstract class Entity implements JsonSerializable, Stringable { return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE); } -} \ No newline at end of file +} diff --git a/src/Entities/Kkt.php b/src/Entities/Kkt.php new file mode 100644 index 0000000..f379d44 --- /dev/null +++ b/src/Entities/Kkt.php @@ -0,0 +1,149 @@ + 'Нет ошибок', + 1 => 'Отсутствует физический канал связи', + 2 => 'Ошибка сетевых настроек или нет соединения с сервером ОФД', + 3 => 'Разрыв соединения при передаче документа на сервер', + 4 => 'Некорректный заголовок сессионного пакета', + 5 => 'Превышен таймаут ожидания квитанции', + 6 => 'Разрыв соединения при приеме квитанции', + 7 => 'Превышен таймаут передачи документа на сервер', + 8 => 'ОФД-процесс не иницилизирован', + ]; + + /** + * @var string[] Список обязательных атрибутов + */ + private array $properties = [ + 'serialNumber', + 'registrationNumber', + 'deviceNumber', + 'fiscalizationDate', + 'fiscalStorageExpiration', + 'signedDocuments', + 'fiscalStoragePercentageUse', + 'fiscalStorageINN', + 'fiscalStorageSerialNumber', + 'fiscalStoragePaymentAddress', + 'groupCode', + 'timestamp', + 'isShiftOpened', + 'shiftNumber', + 'shiftReceipt', + //'unsentDocs', + //'firstUnsetDocTimestamp', + 'networkErrorCode', + ]; + + /** + * @var string[] Массив атрибутов, которые кастуются к DateTime + */ + private array $timestamps = [ + 'fiscalizationDate', + 'fiscalStorageExpiration', + 'firstUnsetDocTimestamp', + 'timestamp', + ]; + + /** + * Конструктор + * + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException + */ + public function __construct(protected \stdClass $data) + { + if (empty((array)$data)) { + throw new EmptyMonitorDataException(); + } + $diff = array_diff($this->properties, array_keys((array)$data)); + if (count($diff) !== 0) { + throw new NotEnoughMonitorDataException($diff); + } + } + + /** + * Эмулирует обращение к атрибутам + * + * @param string $name + * @return null + * @throws Exception + */ + public function __get(string $name) + { + if (empty($this->data?->$name)) { + return null; + } + if (in_array($name, $this->timestamps)) { + return new DateTime($this->data->$name); + } + return $this->data->$name; + } + + /** + * Возвращает объект с информацией о сетевой ошибке + * + * @return object + */ + public function getNetworkError(): object + { + return (object)[ + 'code' => $this->data->networkErrorCode, + 'text' => self::ERROR_CODES[$this->data->networkErrorCode], + ]; + } + + /** + * @inheritDoc + */ + public function jsonSerialize() + { + return $this->data; + } +} diff --git a/src/Entities/Payment.php b/src/Entities/Payment.php index 0a3d356..bc8fd99 100644 --- a/src/Entities/Payment.php +++ b/src/Entities/Payment.php @@ -11,7 +11,7 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Constants\PaymentTypes; +use AtolOnline\Enums\PaymentTypes; /** * Класс, описывающий оплату. Тег ФФД - 1031, 1081, 1215, 1216, 1217. diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index 98dd642..9e85402 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -11,7 +11,7 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Constants\VatTypes; +use AtolOnline\Enums\VatTypes; /** * Класс, описывающий ставку НДС diff --git a/src/Constants/CorrectionTypes.php b/src/Enums/CorrectionTypes.php similarity index 96% rename from src/Constants/CorrectionTypes.php rename to src/Enums/CorrectionTypes.php index e0c1c9c..7cd82d9 100644 --- a/src/Constants/CorrectionTypes.php +++ b/src/Enums/CorrectionTypes.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/DocumentTypes.php b/src/Enums/DocumentTypes.php similarity index 95% rename from src/Constants/DocumentTypes.php rename to src/Enums/DocumentTypes.php index d4573a6..2f93622 100644 --- a/src/Constants/DocumentTypes.php +++ b/src/Enums/DocumentTypes.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/PaymentMethods.php b/src/Enums/PaymentMethods.php similarity index 98% rename from src/Constants/PaymentMethods.php rename to src/Enums/PaymentMethods.php index 498131a..b82db22 100644 --- a/src/Constants/PaymentMethods.php +++ b/src/Enums/PaymentMethods.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/PaymentObjects.php b/src/Enums/PaymentObjects.php similarity index 99% rename from src/Constants/PaymentObjects.php rename to src/Enums/PaymentObjects.php index 313e552..741084d 100644 --- a/src/Constants/PaymentObjects.php +++ b/src/Enums/PaymentObjects.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/PaymentTypes.php b/src/Enums/PaymentTypes.php similarity index 98% rename from src/Constants/PaymentTypes.php rename to src/Enums/PaymentTypes.php index 54a93a2..68ad635 100644 --- a/src/Constants/PaymentTypes.php +++ b/src/Enums/PaymentTypes.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/ReceiptOperationTypes.php b/src/Enums/ReceiptOperationTypes.php similarity index 97% rename from src/Constants/ReceiptOperationTypes.php rename to src/Enums/ReceiptOperationTypes.php index 83ed9f0..2e55820 100644 --- a/src/Constants/ReceiptOperationTypes.php +++ b/src/Enums/ReceiptOperationTypes.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/SnoTypes.php b/src/Enums/SnoTypes.php similarity index 97% rename from src/Constants/SnoTypes.php rename to src/Enums/SnoTypes.php index 4aca641..0892fc6 100644 --- a/src/Constants/SnoTypes.php +++ b/src/Enums/SnoTypes.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Constants/VatTypes.php b/src/Enums/VatTypes.php similarity index 96% rename from src/Constants/VatTypes.php rename to src/Enums/VatTypes.php index d78e0ad..f8e377e 100644 --- a/src/Constants/VatTypes.php +++ b/src/Enums/VatTypes.php @@ -9,7 +9,7 @@ declare(strict_types = 1); -namespace AtolOnline\Constants; +namespace AtolOnline\Enums; use MyCLabs\Enum\Enum; diff --git a/src/Exceptions/EmptyMonitorDataException.php b/src/Exceptions/EmptyMonitorDataException.php new file mode 100644 index 0000000..7aad529 --- /dev/null +++ b/src/Exceptions/EmptyMonitorDataException.php @@ -0,0 +1,23 @@ +message . implode(', ', $props_diff), $code, $previous); + } +} \ No newline at end of file diff --git a/tests/BasicTestCase.php b/tests/BasicTestCase.php index f20ff62..4bd5c23 100644 --- a/tests/BasicTestCase.php +++ b/tests/BasicTestCase.php @@ -46,7 +46,6 @@ class BasicTestCase extends TestCase * Проверяет доступность API мониторинга * * @return bool - * @throws GuzzleException */ protected function isMonitoringOnline(): bool { @@ -54,9 +53,7 @@ class BasicTestCase extends TestCase } /** - * Пропускает текущий тест если API мониторинга недоступно - * - * @throws GuzzleException + * Пропускает текущий тест если API мониторинга недоступен */ protected function skipIfMonitoringIsOffline(): void { @@ -90,7 +87,7 @@ class BasicTestCase extends TestCase * @param object|string $expected * @param object|string $actual */ - public function assertIsSameClass(object|string $expected, object|string $actual) + public function assertIsSameClass(object|string $expected, object|string $actual): void { $this->assertEquals( is_object($expected) ? $expected::class : $expected, @@ -104,7 +101,7 @@ class BasicTestCase extends TestCase * @param string[] $parents * @param object|string $actual */ - public function assertExtendsClasses(array $parents, object|string $actual) + public function assertExtendsClasses(array $parents, object|string $actual): void { $this->checkClassesIntersection($parents, $actual, 'class_parents'); } @@ -115,7 +112,7 @@ class BasicTestCase extends TestCase * @param string[] $parents * @param object|string $actual */ - public function assertImplementsInterfaces(array $parents, object|string $actual) + public function assertImplementsInterfaces(array $parents, object|string $actual): void { $this->checkClassesIntersection($parents, $actual, 'class_implements'); } @@ -126,7 +123,7 @@ class BasicTestCase extends TestCase * @param string[] $parents * @param object|string $actual */ - public function assertUsesTraits(array $parents, object|string $actual) + public function assertUsesTraits(array $parents, object|string $actual): void { $this->checkClassesIntersection($parents, $actual, 'class_uses'); } diff --git a/tests/CompanyTest.php b/tests/CompanyTest.php index 7b8e20c..82961af 100644 --- a/tests/CompanyTest.php +++ b/tests/CompanyTest.php @@ -10,8 +10,8 @@ namespace AtolOnlineTests; use AtolOnline\{ - Constants\SnoTypes, Entities\Company, + Enums\SnoTypes, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, Exceptions\InvalidPaymentAddressException, diff --git a/tests/ItemTest_todo.php b/tests/ItemTest_todo.php index 88a4088..2e3b492 100644 --- a/tests/ItemTest_todo.php +++ b/tests/ItemTest_todo.php @@ -10,10 +10,10 @@ namespace AtolOnlineTests; use AtolOnline\{ - Constants\PaymentMethods, - Constants\PaymentObjects, - Constants\VatTypes, Entities\Item, + Enums\PaymentMethods, + Enums\PaymentObjects, + Enums\VatTypes, Exceptions\BasicTooManyException, Exceptions\TooHighPriceException, Exceptions\TooLongNameException, diff --git a/tests/KktEntityTest.php b/tests/KktEntityTest.php new file mode 100644 index 0000000..b1a2a9c --- /dev/null +++ b/tests/KktEntityTest.php @@ -0,0 +1,153 @@ + '00107703864827', + 'registrationNumber' => '0000000003027865', + 'deviceNumber' => 'KKT024219', + 'fiscalizationDate' => '2019-07-22T14:03:00+00:00', + 'fiscalStorageExpiration' => '2020-11-02T21:00:00+00:00', + 'signedDocuments' => 213350, + 'fiscalStoragePercentageUse' => 85.34, + 'fiscalStorageINN' => '3026455760', + 'fiscalStorageSerialNumber' => '9999078902004339', + 'fiscalStoragePaymentAddress' => 'test.qa.ru', + 'groupCode' => 'test-qa-ru_14605', + 'timestamp' => '2019-12-05T10:45:30+00:00', + 'isShiftOpened' => true, + 'shiftNumber' => 126, + 'shiftReceipt' => 2278, + //'unsentDocs' => 123, + 'firstUnsetDocTimestamp' => 'there must be timestamp, but we want to get exception here to get string', + 'networkErrorCode' => 2, + ]; + + /** + * Тестирует создание объекта ККТ с валидными данными + * + * @covers \AtolOnline\Entities\Kkt::__construct + * @covers \AtolOnline\Entities\Kkt::__get + * @covers \AtolOnline\Entities\Kkt::jsonSerialize + * @covers \AtolOnline\Entities\Kkt::__toString + * @throws Exception + */ + public function testConstructor(): void + { + $kkt = new Kkt((object)$this->sample_data); + $this->assertIsSameClass(Kkt::class, $kkt); + $this->assertAtolable($kkt); + } + + /** + * Тестирует исключение при попытке создать объект ККТ без данных от монитора + * + * @covers \AtolOnline\Entities\Kkt::__construct + * @covers \AtolOnline\Exceptions\EmptyMonitorDataException + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException + */ + public function testEmptyMonitorDataException(): void + { + $this->expectException(EmptyMonitorDataException::class); + new Kkt((object)[]); + } + + /** + * Тестирует исключение при попытке создать объект ККТ без данных от монитора + * + * @covers \AtolOnline\Entities\Kkt::__construct + * @covers \AtolOnline\Exceptions\NotEnoughMonitorDataException + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException + */ + public function testNotEnoughMonitorDataException(): void + { + $this->expectException(NotEnoughMonitorDataException::class); + new Kkt((object)[ + 'fiscalizationDate' => '2021-11-20T10:21:00+00:00', + ]); + } + + /** + * Тестирует получение атрибутов через магический геттер + * + * @covers \AtolOnline\Entities\Kkt::__get + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException + */ + public function testMagicGetter(): void + { + $kkt = new Kkt((object)$this->sample_data); + + // string + $this->assertNotNull($kkt->serialNumber); + $this->assertIsString($kkt->serialNumber); + $this->assertEquals($this->sample_data['serialNumber'], $kkt->serialNumber); + + // int + $this->assertNotNull($kkt->signedDocuments); + $this->assertIsInt($kkt->signedDocuments); + + // float + $this->assertNotNull($kkt->signedDocuments); + $this->assertIsFloat($kkt->fiscalStoragePercentageUse); + + // null + $this->assertNull($kkt->unsentDocs); + + // DateTime + $this->assertNotNull($kkt->fiscalizationDate); + $this->assertIsSameClass(DateTime::class, $kkt->fiscalizationDate); + } + + /** + * Тестирует исключение при попытке получить некорректный DateTime через магический геттер + * + * @covers \AtolOnline\Entities\Kkt::__get + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException + */ + public function testDateTimeException(): void + { + $this->expectException(Exception::class); + (new Kkt((object)$this->sample_data))->firstUnsetDocTimestamp; + } + + /** + * Тестирует получение данных о сетевой ошибке + * + * @covers \AtolOnline\Entities\Kkt::getNetworkError + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException + */ + public function testGetNetworkError(): void + { + $kkt = new Kkt((object)$this->sample_data); + $this->assertIsObject($kkt->getNetworkError()); + $this->assertEquals((object)[ + 'code' => $kkt->networkErrorCode, + 'text' => $kkt::ERROR_CODES[$kkt->networkErrorCode], + ], $kkt->getNetworkError()); + } +} diff --git a/tests/KktMonitorTest.php b/tests/KktMonitorTest.php index a6ebde6..b1ca4d6 100644 --- a/tests/KktMonitorTest.php +++ b/tests/KktMonitorTest.php @@ -5,9 +5,12 @@ namespace AtolOnlineTests; use AtolOnline\Api\AtolClient; use AtolOnline\Api\KktMonitor; use AtolOnline\Api\KktResponse; +use AtolOnline\Entities\Kkt; use AtolOnline\Exceptions\AuthFailedException; use AtolOnline\Exceptions\EmptyLoginException; +use AtolOnline\Exceptions\EmptyMonitorDataException; use AtolOnline\Exceptions\EmptyPasswordException; +use AtolOnline\Exceptions\NotEnoughMonitorDataException; use AtolOnline\Exceptions\TooLongLoginException; use AtolOnline\Exceptions\TooLongPasswordException; use AtolOnline\Helpers; @@ -19,12 +22,29 @@ use GuzzleHttp\Exception\GuzzleException; */ class KktMonitorTest extends BasicTestCase { + /** + * Возвращает объект клиента для тестирования с тестовым API АТОЛ + * + * @return AtolClient + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + private function newTestClient(): KktMonitor + { + $credentials = TestEnvParams::FFD105(); + return (new KktMonitor(true)) + ->setLogin($credentials['login']) + ->setPassword($credentials['password']); + } + /** * Тестирует успешное создание объекта монитора без аргументов конструктора * * @covers \AtolOnline\Api\KktMonitor::__construct */ - public function testConstructorWithoutArgs() + public function testConstructorWithoutArgs(): void { $client = new KktMonitor(); $this->assertIsObject($client); @@ -45,7 +65,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - public function testConstructorWithArgs() + public function testConstructorWithArgs(): void { $client = new KktMonitor(false, 'login', 'password', []); $this->assertIsObject($client); @@ -62,7 +82,7 @@ class KktMonitorTest extends BasicTestCase * @throws EmptyLoginException * @throws TooLongLoginException */ - public function testLogin() + public function testLogin(): void { $client = new KktMonitor(login: 'login'); $this->assertEquals('login', $client->getLogin()); @@ -81,7 +101,7 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::setLogin * @covers \AtolOnline\Exceptions\EmptyLoginException */ - public function testEmptyLoginException() + public function testEmptyLoginException(): void { $this->expectException(EmptyLoginException::class); new KktMonitor(login: ''); @@ -98,7 +118,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - public function testTooLongLoginException() + public function testTooLongLoginException(): void { $this->expectException(TooLongLoginException::class); new KktMonitor(login: Helpers::randomStr(101)); @@ -113,7 +133,7 @@ class KktMonitorTest extends BasicTestCase * @throws EmptyPasswordException * @throws TooLongPasswordException */ - public function testPassword() + public function testPassword(): void { $client = new KktMonitor(password: 'password'); $this->assertEquals('password', $client->getPassword()); @@ -132,7 +152,7 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::setPassword * @covers \AtolOnline\Exceptions\EmptyPasswordException */ - public function testEmptyPasswordException() + public function testEmptyPasswordException(): void { $this->expectException(EmptyPasswordException::class); new KktMonitor(password: ''); @@ -148,7 +168,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - public function testConstructorWithLongPassword() + public function testConstructorWithLongPassword(): void { $this->expectException(TooLongPasswordException::class); new KktMonitor(password: Helpers::randomStr(101)); @@ -161,7 +181,7 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::isTestMode * @covers \AtolOnline\Api\KktMonitor::setTestMode */ - public function testTestMode() + public function testTestMode(): void { $client = new KktMonitor(); $this->assertTrue($client->isTestMode()); @@ -182,23 +202,6 @@ class KktMonitorTest extends BasicTestCase $this->assertFalse($client->isTestMode()); } - /** - * Возвращает объект клиента для тестирования с тестовым API АТОЛ - * - * @return AtolClient - * @throws EmptyLoginException - * @throws EmptyPasswordException - * @throws TooLongLoginException - * @throws TooLongPasswordException - */ - private function newTestClient(): KktMonitor - { - $credentials = TestEnvParams::FFD105(); - return (new KktMonitor(true)) - ->setLogin($credentials['login']) - ->setPassword($credentials['password']); - } - /** * Тестирует авторизацию * @@ -207,6 +210,7 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\KktMonitor::getAuthEndpoint * @covers \AtolOnline\Api\KktMonitor::doAuth * @covers \AtolOnline\Api\KktMonitor::auth + * @covers \AtolOnline\Exceptions\AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws TooLongLoginException @@ -214,7 +218,7 @@ class KktMonitorTest extends BasicTestCase * @throws AuthFailedException * @throws GuzzleException */ - public function testAuth() + public function testAuth(): void { $this->skipIfMonitoringIsOffline(); $result = $this->newTestClient()->auth(); @@ -227,6 +231,7 @@ class KktMonitorTest extends BasicTestCase * @depends testAuth * @covers \AtolOnline\Api\KktMonitor::setToken * @covers \AtolOnline\Api\KktMonitor::getToken + * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -234,7 +239,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - public function testGetToken() + public function testGetToken(): void { $client = new KktMonitor(); $this->assertNull($client->getToken()); @@ -250,6 +255,7 @@ class KktMonitorTest extends BasicTestCase * * @depends testAuth * @covers \AtolOnline\Api\KktMonitor::getResponse + * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -257,7 +263,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - public function testGetResponse() + public function testGetResponse(): void { $this->skipIfMonitoringIsOffline(); $client = $this->newTestClient(); @@ -273,6 +279,7 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\AtolClient::getUrlToMethod * @covers \AtolOnline\Api\KktMonitor::fetchAll * @covers \AtolOnline\Api\KktMonitor::getAll + * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -280,7 +287,7 @@ class KktMonitorTest extends BasicTestCase * @throws TooLongLoginException * @throws TooLongPasswordException */ - public function testMonitorGetAll() + public function testMonitorGetAll(): void { $this->skipIfMonitoringIsOffline(); $client = $this->newTestClient(); @@ -289,6 +296,7 @@ class KktMonitorTest extends BasicTestCase $this->assertNotEmpty($client->getResponse()->getContent()); $this->assertIsCollection($kkts); $this->assertTrue($kkts->count() > 0); + $this->assertIsSameClass(Kkt::class, $kkts->random()); } /** @@ -299,24 +307,30 @@ class KktMonitorTest extends BasicTestCase * @covers \AtolOnline\Api\AtolClient::getUrlToMethod * @covers \AtolOnline\Api\KktMonitor::fetchOne * @covers \AtolOnline\Api\KktMonitor::getOne + * @covers \AtolOnline\Entities\Kkt::__construct + * @covers \AtolOnline\Entities\Kkt::__get + * @covers \AtolOnline\Entities\Kkt::jsonSerialize + * @covers \AtolOnline\Entities\Kkt::__toString * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws GuzzleException * @throws TooLongLoginException * @throws TooLongPasswordException + * @throws EmptyMonitorDataException + * @throws NotEnoughMonitorDataException */ - public function testGetOne() + public function testMOnitorGetOne(): void { $this->skipIfMonitoringIsOffline(); $client = $this->newTestClient(); $client->auth(); - $kkts = $client->getAll(); - $serial_number = $kkts->first()->serialNumber; - $client->getOne($serial_number); - $this->assertIsSameClass(KktResponse::class, $client->getResponse()); + $serial_number = $client->getAll()->random()->serialNumber; + $kkt = $client->getOne($serial_number); $this->assertNotEmpty($client->getResponse()); - $this->assertNotNull($client->getResponse()->data->serialNumber); - $this->assertEquals($serial_number, $client->getResponse()->data->serialNumber); + $this->assertIsSameClass(Kkt::class, $kkt); + $this->assertAtolable($kkt); + $this->assertNotNull($kkt->serialNumber); + $this->assertEquals($serial_number, $kkt->serialNumber); } } diff --git a/tests/VatTest_todo.php b/tests/VatTest_todo.php index b65fee9..17eaa01 100644 --- a/tests/VatTest_todo.php +++ b/tests/VatTest_todo.php @@ -10,9 +10,8 @@ namespace AtolOnlineTests; use AtolOnline\{ - Constants\VatTypes, - Entities\Vat -}; + Entities\Vat, + Enums\VatTypes}; /** * Class VatTest From b35b9bfa87ab40fb24562d51c9eca96adbea8018 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 20 Nov 2021 23:46:39 +0800 Subject: [PATCH 08/85] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B0?= =?UTF-8?q?=20base=5Fname=20=D0=B2=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/CorrectionInfo.php | 81 +++++++++------------------------ 1 file changed, 22 insertions(+), 59 deletions(-) diff --git a/src/Entities/CorrectionInfo.php b/src/Entities/CorrectionInfo.php index d6f11e6..07f4122 100644 --- a/src/Entities/CorrectionInfo.php +++ b/src/Entities/CorrectionInfo.php @@ -20,46 +20,34 @@ class CorrectionInfo extends Entity * @var string Тип коррекции. Тег ФФД - 1173. */ protected string $type; - + /** * @var string Дата документа основания для коррекции. Тег ФФД - 1178. */ protected string $base_date; - + /** * @var string Номер документа основания для коррекции. Тег ФФД - 1179. */ protected string $base_number; - - /** - * @var string Описание коррекции. Тег ФФД - 1177. - */ - protected string $base_name; - + /** * CorrectionInfo constructor. * - * @param string|null $type Тип коррекции - * @param string|null $base_date Дата документа + * @param string|null $type Тип коррекции + * @param string|null $base_date Дата документа * @param string|null $base_number Номер документа - * @param string|null $base_name Описание коррекции */ - public function __construct(?string $type = null, ?string $base_date = null, ?string $base_number = null, ?string $base_name = null) - { - if ($type) { - $this->setType($type); - } - if ($base_date) { - $this->setDate($base_date); - } - if ($base_number) { - $this->setNumber($base_number); - } - if ($base_name) { - $this->setName($base_name); - } + public function __construct( + ?string $type = null, + ?string $base_date = null, + ?string $base_number = null + ) { + $type && $this->setType($type); + $base_date && $this->setDate($base_date); + $base_number && $this->setNumber($base_number); } - + /** * Возвращает номер документа основания для коррекции. * Тег ФФД - 1179. @@ -70,7 +58,7 @@ class CorrectionInfo extends Entity { return $this->base_number; } - + /** * Устанавливает номер документа основания для коррекции. * Тег ФФД - 1179. @@ -83,31 +71,7 @@ class CorrectionInfo extends Entity $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): CorrectionInfo - { - $this->base_name = trim($name); - return $this; - } - + /** * Возвращает дату документа основания для коррекции. * Тег ФФД - 1178. @@ -118,7 +82,7 @@ class CorrectionInfo extends Entity { return $this->base_date; } - + /** * Устанавливает дату документа основания для коррекции. * Тег ФФД - 1178. @@ -131,7 +95,7 @@ class CorrectionInfo extends Entity $this->base_date = $date; return $this; } - + /** * Возвращает тип коррекции. * Тег ФФД - 1173. @@ -142,7 +106,7 @@ class CorrectionInfo extends Entity { return $this->type; } - + /** * Устанавливает тип коррекции. * Тег ФФД - 1173. @@ -155,17 +119,16 @@ class CorrectionInfo extends Entity $this->type = $type; return $this; } - + /** * @inheritDoc */ - public function jsonSerialize() + public function jsonSerialize(): object { - return [ + return (object)[ 'type' => $this->getType() ?? '', // обязателен 'base_date' => $this->getDate() ?? '', // обязателен 'base_number' => $this->getNumber() ?? '', // обязателен - 'base_name' => $this->getName() ?? '' // не обязателен ]; } } \ No newline at end of file From e89369348a13e0f7814bf5accd42a07c38e67b62 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 00:15:59 +0800 Subject: [PATCH 09/85] =?UTF-8?q?=D0=9C=D0=B5=D1=82=D0=BE=D0=B4=20KktMonit?= =?UTF-8?q?or::auth()=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D1=91?= =?UTF-8?q?=D0=BD=20=D0=B2=20AtolClient=20=D0=B4=D0=BB=D1=8F=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B8=D0=BC=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D0=B2=20=D1=84=D0=B8=D1=81=D0=BA=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=82=D0=BE=D1=80=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/AtolClient.php | 14 ++++++++++++-- src/Api/KktMonitor.php | 15 --------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index 213e790..51254e1 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -121,7 +121,7 @@ abstract class AtolClient * @param string|null $token * @return $this */ - public function setToken(?string $token): AtolClient + public function setToken(?string $token): self { $this->token = $token; return $this; @@ -283,7 +283,17 @@ abstract class AtolClient * @throws TooLongPasswordException * @throws GuzzleException */ - abstract public function auth(?string $login = null, ?string $password = null): bool; + public function auth(?string $login = null, ?string $password = null): bool + { + if (empty($this->getToken())) { + $login && $this->setLogin($login); + $password && $this->setPassword($password); + if ($token = $this->doAuth()) { + $this->setToken($token); + } + } + return !empty($this->getToken()); + } /** * Возвращает URL для запроса авторизации diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index f1fb41e..037af92 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -44,21 +44,6 @@ class KktMonitor extends AtolClient : 'https://online.atol.ru/api/kkt/v1'; } - /** - * @inheritDoc - */ - public function auth(?string $login = null, ?string $password = null): bool - { - if (empty($this->getToken())) { - $login && $this->setLogin($login); - $password && $this->setPassword($password); - if ($token = $this->doAuth()) { - $this->setToken($token); - } - } - return !empty($this->getToken()); - } - /** * Получает от API информацию обо всех ККТ и ФН в рамках группы * From 6620acf1bf2b9a8a54a5a9e5b976268c05ef2fe5 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 00:17:40 +0800 Subject: [PATCH 10/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=20KktMonito?= =?UTF-8?q?r::GetOne()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/KktMonitorTest.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/KktMonitorTest.php b/tests/KktMonitorTest.php index b1ca4d6..88d9ad5 100644 --- a/tests/KktMonitorTest.php +++ b/tests/KktMonitorTest.php @@ -1,4 +1,11 @@ skipIfMonitoringIsOffline(); $client = $this->newTestClient(); $client->auth(); - $serial_number = $client->getAll()->random()->serialNumber; + $serial_number = $client->getAll()->first()->serialNumber; $kkt = $client->getOne($serial_number); $this->assertNotEmpty($client->getResponse()); $this->assertIsSameClass(Kkt::class, $kkt); From 6d0cac2cbc2e05a82492f01e2aa3fdceb63ccf5c Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 00:19:35 +0800 Subject: [PATCH 11/85] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D1=80=D0=B5?= =?UTF-8?q?=D1=84=D0=B0=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=D0=B0=20=D1=84?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/Kkt.php | 577 -------------------------------------- src/Api/KktFiscalizer.php | 378 +++++++++++++++++++++++++ 2 files changed, 378 insertions(+), 577 deletions(-) delete mode 100644 src/Api/Kkt.php create mode 100644 src/Api/KktFiscalizer.php diff --git a/src/Api/Kkt.php b/src/Api/Kkt.php deleted file mode 100644 index 89921ff..0000000 --- a/src/Api/Kkt.php +++ /dev/null @@ -1,577 +0,0 @@ -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): Kkt - { - $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 EmptyLoginException Логин ККТ не может быть пустым - * @throws TooLongLoginException Слишком длинный логин ККТ - */ - public function setLogin(string $login): Kkt - { - if (empty($login)) { - throw new EmptyLoginException(); - } elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) { - throw new TooLongLoginException($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 EmptyPasswordException Пароль ККТ не может быть пустым - * @throws TooLongPasswordException Слишком длинный пароль ККТ - */ - public function setPassword(string $password): Kkt - { - if (empty($password)) { - throw new EmptyPasswordException(); - } elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) { - throw new TooLongPasswordException($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 TooLongCallbackUrlException Слишком длинный Callback URL - * @throws InvalidCallbackUrlException Невалидный Callback URL - */ - public function setCallbackUrl(string $url): Kkt - { - if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) { - throw new TooLongCallbackUrlException($url, Constraints::MAX_LENGTH_CALLBACK_URL); - } elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) { - throw new InvalidCallbackUrlException('Callback URL not matches with pattern'); - } - $this->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 bool - */ - public function isTestMode(): bool - { - return $this->is_test_mode; - } - - /** - * Устанавливает флаг тестового режима - * - * @param bool $test_mode - * @return $this - */ - public function setTestMode(bool $test_mode = true): Kkt - { - $this->is_test_mode = $test_mode; - return $this; - } - - /** - * Регистрирует документ прихода - * - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе есть данные коррекции - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws GuzzleException - */ - public function sell(Document $document, ?string $external_id = null): KktResponse - { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Некорректная операция над документом коррекции'); - } - return $this->registerDocument('sell', 'receipt', $document, $external_id); - } - - /** - * Регистрирует документ возврата прихода - * - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе есть данные коррекции - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws TooManyVatsException Слишком много ставок НДС - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws GuzzleException - */ - public function sellRefund(Document $document, ?string $external_id = null): KktResponse - { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Invalid operation on correction document'); - } - return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id); - } - - /** - * Регистрирует документ коррекции прихода - * - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws TooManyItemsException Слишком много предметов расчёта - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws GuzzleException - */ - public function sellCorrection(Document $document, ?string $external_id = null): KktResponse - { - if (!$document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException(); - } - $document->setClient(null)->setItems([]); - return $this->registerDocument('sell_correction', 'correction', $document, $external_id); - } - - /** - * Регистрирует документ расхода - * - * @param Document $document - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе есть данные коррекции - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws GuzzleException - */ - public function buy(Document $document, ?string $external_id = null): KktResponse - { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Invalid operation on correction document'); - } - return $this->registerDocument('buy', 'receipt', $document, $external_id); - } - - /** - * Регистрирует документ возврата расхода - * - * @param Document $document - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе есть данные коррекции - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws TooManyVatsException Слишком много ставок НДС - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws GuzzleException - */ - public function buyRefund(Document $document, ?string $external_id = null): KktResponse - { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Invalid operation on correction document'); - } - return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id); - } - - /** - * Регистрирует документ коррекции расхода - * - * @param Document $document - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции - * @throws InvalidInnLengthException Некорректная длтина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws TooManyItemsException Слишком много предметов расчёта - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws GuzzleException - */ - public function buyCorrection(Document $document, ?string $external_id = null): KktResponse - { - if (!$document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException(); - } - $document->setClient(null)->setItems([]); - return $this->registerDocument('buy_correction', 'correction', $document, $external_id); - } - - /** - * Проверяет статус чека на ККТ один раз - * - * @param string $uuid UUID регистрации - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws InvalidUuidException Некорректный UUID документа - * @throws GuzzleException - */ - public function getDocumentStatus(string $uuid): KktResponse - { - $uuid = trim($uuid); - if (!Uuid::isValid($uuid)) { - throw new InvalidUuidException($uuid); - } - $this->auth(); - return $this->sendAtolRequest('GET', 'report/' . $uuid); - } - - /** - * Проверяет статус чека на ККТ нужное количество раз с указанным интервалом. - * Вернёт результат как только при очередной проверке сменится статус регистрации документа. - * - * @param string $uuid UUID регистрации - * @param int $retry_count Количество попыток - * @param int $timeout Таймаут в секундах между попытками - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws InvalidUuidException Некорректный UUID документа - * @throws GuzzleException - */ - public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): KktResponse - { - $try = 0; - do { - $response = $this->getDocumentStatus($uuid); - if ($response->isValid() && $response->getContent()->status == 'done') { - break; - } else { - sleep($timeout); - } - ++$try; - } while ($try < $retry_count); - return $response; - } - - /** - * Возвращает текущий токен авторизации - * - * @return string - */ - public function getAuthToken(): ?string - { - return $this->auth_token; - } - - /** - * Устанавливает заранее известный токен авторизации - * - * @param string|null $auth_token - * @return $this - */ - public function setAuthToken(?string $auth_token): Kkt - { - $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::FFD105()['group']; - $this->kkt_config['test']['login'] = TestEnvParams::FFD105()['login']; - $this->kkt_config['test']['pass'] = TestEnvParams::FFD105()['password']; - $this->kkt_config['test']['url'] = 'https://testonline.atol.ru/possystem/v4'; - $this->kkt_config['test']['callback_url'] = ''; - } - - /** - * Возвращает набор заголовков для HTTP-запроса - * - * @return array - */ - protected function getHeaders(): array - { - $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): string - { - $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 KktResponse - * @throws 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 - ): KktResponse { - $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 AuthFailedException Ошибка авторизации - * @throws GuzzleException - */ - protected function auth(): bool - { - if (!$this->getAuthToken()) { - $result = $this->sendAtolRequest('GET', 'getToken', [ - 'login' => $this->getLogin(), - 'pass' => $this->getPassword(), - ]); - if (!$result->isValid() || !$result->getContent()->token) { - throw new AuthFailedException($result); - } - $this->auth_token = $result->getContent()->token; - } - return true; - } - - /** - * Отправляет документ на регистрацию - * - * @param string $api_method Метод API - * @param string $type Тип документа: receipt, correction - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws GuzzleException - * @throws Exception - */ - protected function registerDocument( - string $api_method, - string $type, - Document $document, - ?string $external_id = null - ): KktResponse { - $type = trim($type); - if (!in_array($type, ['receipt', 'correction'])) { - throw new InvalidDocumentTypeException($type); - } - $this->auth(); - if ($this->isTestMode()) { - $document->setCompany(($document->getCompany() ?: new Company()) - ->setInn(TestEnvParams::FFD105()['inn']) - ->setSno(TestEnvParams::FFD105()['sno']) - ->setPaymentAddress(TestEnvParams::FFD105()['payment_address'])); - } - $data['timestamp'] = date('d.m.y H:i:s'); - $data['external_id'] = $external_id ?: Uuid::uuid4()->toString(); - $data[$type] = $document; - if ($this->getCallbackUrl()) { - $data['service'] = ['callback_url' => $this->getCallbackUrl()]; - } - return $this->sendAtolRequest('POST', trim($api_method), $data); - } -} diff --git a/src/Api/KktFiscalizer.php b/src/Api/KktFiscalizer.php new file mode 100644 index 0000000..47d43e3 --- /dev/null +++ b/src/Api/KktFiscalizer.php @@ -0,0 +1,378 @@ +setGroup($group); + } + + /** + * Устанавливает группу доступа к ККТ + * + * @param string $group + * @return $this + */ + public function setGroup(string $group): self + { + // критерии к длине строки не описаны ни в схеме, ни в документации + $this->group = $group; + return $this; + } + + /** + * Возвращает группу доступа к ККТ в соответствии с флагом тестового режима + * + * @return string|null + */ + public function getGroup(): ?string + { + return $this->group; + } + + /** + * Устанавливает URL для приёма колбеков + * + * @param string $url + * @return $this + * @throws TooLongCallbackUrlException + * @throws InvalidCallbackUrlException + */ + public function setCallbackUrl(string $url): self + { + if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) { + throw new TooLongCallbackUrlException($url, Constraints::MAX_LENGTH_CALLBACK_URL); + } elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) { + throw new InvalidCallbackUrlException('Callback URL not matches with pattern'); + } + $this->callback_url = $url; + return $this; + } + + /** + * Возвращает URL для приёма колбеков + * + * @return string + */ + public function getCallbackUrl(): string + { + return $this->callback_url; + } + + /** + * Регистрирует документ прихода + * + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyCorrectionInfoException + * @throws InvalidInnLengthException + * @throws TooLongPaymentAddressException + * @throws InvalidDocumentTypeException + * @throws GuzzleException + */ + public function sell(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Некорректная операция над документом коррекции'); + } + return $this->registerDocument('sell', 'receipt', $document, $external_id); + } + + /** + * Регистрирует документ возврата прихода + * + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyCorrectionInfoException + * @throws InvalidInnLengthException + * @throws TooLongPaymentAddressException + * @throws TooManyVatsException + * @throws InvalidDocumentTypeException + * @throws GuzzleException + */ + public function sellRefund(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Invalid operation on correction document'); + } + return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id); + } + + /** + * Регистрирует документ коррекции прихода + * + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyCorrectionInfoException + * @throws InvalidInnLengthException + * @throws TooLongPaymentAddressException + * @throws TooManyItemsException + * @throws InvalidDocumentTypeException + * @throws GuzzleException + */ + public function sellCorrection(Document $document, ?string $external_id = null): KktResponse + { + if (!$document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException(); + } + $document->setClient(null)->setItems([]); + return $this->registerDocument('sell_correction', 'correction', $document, $external_id); + } + + /** + * Регистрирует документ расхода + * + * @param Document $document + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyCorrectionInfoException + * @throws InvalidInnLengthException + * @throws TooLongPaymentAddressException + * @throws InvalidDocumentTypeException + * @throws GuzzleException + */ + public function buy(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Invalid operation on correction document'); + } + return $this->registerDocument('buy', 'receipt', $document, $external_id); + } + + /** + * Регистрирует документ возврата расхода + * + * @param Document $document + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyCorrectionInfoException + * @throws InvalidInnLengthException + * @throws TooLongPaymentAddressException + * @throws TooManyVatsException + * @throws InvalidDocumentTypeException + * @throws GuzzleException + */ + public function buyRefund(Document $document, ?string $external_id = null): KktResponse + { + if ($document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException('Invalid operation on correction document'); + } + return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id); + } + + /** + * Регистрирует документ коррекции расхода + * + * @param Document $document + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции + * @throws InvalidInnLengthException Некорректная длтина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws TooManyItemsException Слишком много предметов расчёта + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws GuzzleException + */ + public function buyCorrection(Document $document, ?string $external_id = null): KktResponse + { + if (!$document->getCorrectionInfo()) { + throw new EmptyCorrectionInfoException(); + } + $document->setClient(null)->setItems([]); + return $this->registerDocument('buy_correction', 'correction', $document, $external_id); + } + + /** + * Проверяет статус чека на ККТ один раз + * + * @param string $uuid UUID регистрации + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidUuidException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function getDocumentStatus(string $uuid): KktResponse + { + $uuid = trim($uuid); + if (!Uuid::isValid($uuid)) { + throw new InvalidUuidException($uuid); + } + $this->auth(); + return $this->sendRequest('GET', 'report/' . $uuid); + } + + /** + * Проверяет статус чека на ККТ нужное количество раз с указанным интервалом. + * Вернёт результат как только при очередной проверке сменится статус регистрации документа. + * + * @param string $uuid UUID регистрации + * @param int $retry_count Количество попыток + * @param int $timeout Таймаут в секундах между попытками + * @return KktResponse + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidUuidException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): KktResponse + { + $try = 0; + do { + $response = $this->getDocumentStatus($uuid); + if ($response->isValid() && $response->getContent()->status == 'done') { + break; + } else { + sleep($timeout); + } + ++$try; + } while ($try < $retry_count); + return $response; + } + + /** + * Отправляет документ на регистрацию + * + * @param string $api_method Метод API + * @param string $type Тип документа: receipt, correction + * @param Document $document Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) + * @return KktResponse + * @throws AuthFailedException Ошибка авторизации + * @throws InvalidDocumentTypeException Некорректный тип документа + * @throws InvalidInnLengthException Некорректная длина ИНН + * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @throws GuzzleException + * @throws Exception + */ + protected function registerDocument( + string $api_method, + string $type, + Document $document, + ?string $external_id = null + ): KktResponse { + $type = trim($type); + if (!in_array($type, ['receipt', 'correction'])) { + throw new InvalidDocumentTypeException($type); + } + $this->auth(); + if ($this->isTestMode()) { + $document->setCompany(new Company( + 'test@example.com', + TestEnvParams::FFD105()['sno'], + TestEnvParams::FFD105()['inn'], + TestEnvParams::FFD105()['payment_address'], + )); + } + $data['timestamp'] = date('d.m.y H:i:s'); + $data['external_id'] = $external_id ?: Uuid::uuid4()->toString(); + $data[$type] = $document; + if ($this->getCallbackUrl()) { + $data['service'] = ['callback_url' => $this->getCallbackUrl()]; + } + return $this->sendRequest('POST', trim($api_method), $data); + } + + /** + * @inheritDoc + */ + protected function getAuthEndpoint(): string + { + return $this->isTestMode() + ? 'https://testonline.atol.ru/possystem/v1/getToken' + : 'https://online.atol.ru/possystem/v1/getToken'; + } + + /** + * @inheritDoc + */ + protected function getMainEndpoint(): string + { + return $this->isTestMode() + ? 'https://testonline.atol.ru/possystem/v4/' + : 'https://online.atol.ru/possystem/v4/'; + } +} From 95499174b0f3be143dc1b70997d1f2d45ba9ffc6 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 00:33:43 +0800 Subject: [PATCH 12/85] Warning in readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 5043c89..7da0f09 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ # АТОЛ Онлайн +--- + +**В этой ветке проводится глубокий рефакторинг и активная подготовка к `v1.0.0`.** + +**Актуальность документации -- околонулевая.** + +**Общая работоспособность -- нулевая.** + +--- + [![Master build](https://github.com/anthonyaxenov/atol-online/actions/workflows/master.yml/badge.svg)](https://github.com/anthonyaxenov/atol-online/actions/workflows/master.yml) [![Dev build](https://github.com/anthonyaxenov/atol-online/actions/workflows/dev.yml/badge.svg)](https://github.com/anthonyaxenov/atol-online/actions/workflows/dev.yml) From d2810719705e72735c859d5471750674eeb8f586 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 18:57:20 +0800 Subject: [PATCH 13/85] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D1=91=D0=BD=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20`AtolOnline\Entities\Kkt::getN?= =?UTF-8?q?etworkError()`=20=D0=B7=D0=B0=20=D0=BD=D0=B5=D0=BD=D0=B0=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D1=81=D1=82=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Kkt.php | 13 ------------- tests/KktEntityTest.php | 17 ----------------- 2 files changed, 30 deletions(-) diff --git a/src/Entities/Kkt.php b/src/Entities/Kkt.php index f379d44..3178188 100644 --- a/src/Entities/Kkt.php +++ b/src/Entities/Kkt.php @@ -126,19 +126,6 @@ final class Kkt extends Entity return $this->data->$name; } - /** - * Возвращает объект с информацией о сетевой ошибке - * - * @return object - */ - public function getNetworkError(): object - { - return (object)[ - 'code' => $this->data->networkErrorCode, - 'text' => self::ERROR_CODES[$this->data->networkErrorCode], - ]; - } - /** * @inheritDoc */ diff --git a/tests/KktEntityTest.php b/tests/KktEntityTest.php index b1a2a9c..6f2cfd7 100644 --- a/tests/KktEntityTest.php +++ b/tests/KktEntityTest.php @@ -133,21 +133,4 @@ class KktEntityTest extends BasicTestCase $this->expectException(Exception::class); (new Kkt((object)$this->sample_data))->firstUnsetDocTimestamp; } - - /** - * Тестирует получение данных о сетевой ошибке - * - * @covers \AtolOnline\Entities\Kkt::getNetworkError - * @throws EmptyMonitorDataException - * @throws NotEnoughMonitorDataException - */ - public function testGetNetworkError(): void - { - $kkt = new Kkt((object)$this->sample_data); - $this->assertIsObject($kkt->getNetworkError()); - $this->assertEquals((object)[ - 'code' => $kkt->networkErrorCode, - 'text' => $kkt::ERROR_CODES[$kkt->networkErrorCode], - ], $kkt->getNetworkError()); - } } From 920c08c610ec6b2c1da335e033c87eb993f3f56e Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 18:58:03 +0800 Subject: [PATCH 14/85] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - добавлено про мониторинг - убрано про `base_name` из коррекции --- docs/client.md | 2 +- docs/correction_info.md | 6 +-- docs/monitoring.md | 103 ++++++++++++++++++++++++++++++++++++++++ docs/readme.md | 5 +- 4 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 docs/monitoring.md diff --git a/docs/client.md b/docs/client.md index d22484a..01be506 100644 --- a/docs/client.md +++ b/docs/client.md @@ -80,4 +80,4 @@ $json_array = $customer->jsonSerialize(); --- -[Вернуться к содержанию](readme.md) \ No newline at end of file +[Вернуться к содержанию](readme.md) diff --git a/docs/correction_info.md b/docs/correction_info.md index dc67a09..c90cade 100644 --- a/docs/correction_info.md +++ b/docs/correction_info.md @@ -13,8 +13,7 @@ $info = new AtolOnline\Entities\CorrectionInfo(); У объекта должны быть указаны все следующие обязательные атрибуты: * тип коррекции (тег ФФД 1173) - все типы перечислены в классе `AtolOnline\Constants\CorrectionTypes`; * дата документа основания для коррекции в формате `d.m.Y` (тег ФФД 1178); -* номер документа основания для коррекции (тег ФФД 1179); -* описание коррекции (тег ФФД 1177). +* номер документа основания для коррекции (тег ФФД 1179). Указать эти атрибуты можно двумя способами: @@ -26,14 +25,12 @@ $info = new CorrectionInfo( CorrectionTypes::SELF, // тип коррекции '01.01.2019', // дата документа коррекции '12345', // номер документа коррекции - 'test' // описание коррекции ); // 2 способ - через сеттеры $info = (new CorrectionInfo()) ->setType(CorrectionTypes::INSTRUCTION) ->setDate('01.01.2019') - ->setName('test') ->setNumber('9999'); // либо комбинация этих способов @@ -44,7 +41,6 @@ $info = (new CorrectionInfo()) ```php $info->getType(); $info->getDate(); -$info->getName(); $info->getNumber(); ``` diff --git a/docs/monitoring.md b/docs/monitoring.md new file mode 100644 index 0000000..9addf52 --- /dev/null +++ b/docs/monitoring.md @@ -0,0 +1,103 @@ +# Мониторинг ККТ + +[Вернуться к содержанию](readme.md) + +--- + +Библиотека предоставляет возможность следить за состоянием ваших облачных ККТ через API. + +## Инициализация + +Для этого следует использовать класс `KktMonitor`: + +```php +// можно передать параметры подключения в конструктор +$monitor = new AtolOnline\Api\KktMonitor( + login: 'mylogin', + password: 'qwerty' +); + +// можно - отдельными сеттерами +$monitor = new AtolOnline\Api\KktMonitor(); + ->setLogin($credentials['login']) + ->setPassword($credentials['password']); +``` + +Логин и пароль для мониторинга те же, что для регистрации документов. + +**По умолчанию монитор работает в тестовом режиме.** +Перевести его в боевой режим можно: + +```php +// передачей в конструктор `false` первым параметром: +$monitor = new AtolOnline\Api\KktMonitor(false, /*...*/); + +// или отдельным сеттером +$monitor->setTestMode(false); +``` + +**Тестовый режим** нужен для проверки работоспособности библиотеки и API АТОЛ. + +**В боевом режиме** можно получать данные по своим ККТ. + +## Получение данных обо всех своих ККТ + +Для получения данных обо всех своих ККТ следует вызвать метод `AtolOnline\Api\KktMonitor::getAll()`: + +```php +$kkts = $monitor->getAll(); +``` + +В ответе будет итерируемая коллекция объектов `AtolOnline\Entities\Kkt`. Каждый из этих объектов содержит атрибуты: + +```php +// для примера получим первую ККТ из всех +$kkt = $kkts->first(); + +// посмотрим на её атрибуты: +$kkt->serialNumber; // Заводской номер ККТ +$kkt->registrationNumber; // Регистрационный номер машины (РНМ) +$kkt->deviceNumber; // Номер автоматического устройства (внутренний идентификатор устройства) +$kkt->fiscalizationDate; // Дата активации (фискализации) ФН с указанием таймзоны +$kkt->fiscalStorageExpiration; // Дата замены ФН (Срок действия ФН), с указанием таймзоны +$kkt->signedDocuments; // Количество подписанных документов в ФН +$kkt->fiscalStoragePercentageUse; // Наполненость ФН в % +$kkt->fiscalStorageINN; // ИНН компании (указанный в ФН) +$kkt->fiscalStorageSerialNumber; // Заводской (серийный) номер ФН +$kkt->fiscalStoragePaymentAddress; // Адрес расчёта, указанный в ФН +$kkt->groupCode; // Код группы кассы +$kkt->timestamp; // Время и дата формирования данных, UTC +$kkt->isShiftOpened; // Признак открыта смена (true) или закрыта (false) +$kkt->shiftNumber; // Номер смены (или "Номер закрытой смены", когда смена закрыта) +$kkt->shiftReceipt; // Номер документа за смену (или "Кол-во чеков закрытой смены", когда смена закрыта) +$kkt->unsentDocs; // Количество неотправленных документов. Указывается, если значение отлично от 0. +$kkt->firstUnsetDocTimestamp; // Дата первого неотправленного документа. Указывается, если есть неотправленные документы. +$kkt->networkErrorCode; // Код ошибки сети +``` + +Эти поля описаны в документации мониторинга на [стр. 11](https://online.atol.ru/files/API_service_information.pdf) + +Сопоставления кодов ошибок и их описаний доступны в массиве `AtolOnline\Entities\Kkt::ERROR_CODES`. + +## Получение данных об одной из своих ККТ + +Для этого следует вызвать метод `AtolOnline\Api\KktMonitor::getOne()`, передав на вход серийный номер (`serialNumber`) +нужной ККТ: + +```php +$kkt = $monitor->getOne($kkts->first()->serialNumber); +``` + +Метод вернёт единственный объект `AtolOnline\Entities\Kkt` с атрибутами, описанными выше. + +## Получение последнего ответа от сервера + +Класс `AtolOnline\Api\KktMonitor` расширяет абстрактный класс `AtolOnline\Api\AtolClient`. + +Это значит, что последний ответ от API АТОЛ всегда сохраняется объектом класса `AtolOnline\Api\KktResponse`. К нему +можно обратиться через метод `AtolOnline\Api\KktMonitor::getResponse()`, независимо от того, что возвращают другие +методы монитора. + +--- + +[Вернуться к содержанию](readme.md) \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md index 0887090..3375be6 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,4 +1,4 @@ -# Документация к библиотеке atol-online +# Документация к библиотеке Содержание: 1. [Работа с клиентами (покупателями)](client.md) @@ -9,7 +9,8 @@ 6. [Работа с данными коррекции](correction_info.md) 7. [Работа с документами](documents.md) 8. [Работа с ККТ](kkt.md) +9. [Мониторинг ККТ](monitoring.md) --- -Если вы нашли опечатку или какое-то несоответствие — делайте pull-request. +Если вы нашли опечатку или какое-то несоответствие — делайте pull-request. From e1303f4fdbda75d1cf60afcd1f10ddd92697f730 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 21 Nov 2021 19:06:55 +0800 Subject: [PATCH 15/85] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - в мониторинг добавлено о приведении к строке - актуализированы Client и Company - мелочи в соотв. классах --- docs/client.md | 24 ++++-------------------- docs/company.md | 21 ++++----------------- docs/monitoring.md | 13 +++++++++++++ src/Entities/Client.php | 29 +++++++++++++++-------------- src/Entities/Company.php | 4 ++-- 5 files changed, 38 insertions(+), 53 deletions(-) diff --git a/docs/client.md b/docs/client.md index 01be506..73072ed 100644 --- a/docs/client.md +++ b/docs/client.md @@ -25,37 +25,21 @@ $customer = new AtolOnline\Entities\Client(); // 1 способ - через конструктор $customer = new AtolOnline\Entities\Client( 'John Doe', // наименование - '+1/22/99*73s dsdas654 5s6', // номер телефона +122997365456 'john@example.com', // email + '+1/22/99*73s dsdas654 5s6', // номер телефона +122997365456 '+fasd3\qe3fs_=nac990139928czc' // номер ИНН 3399013928 ); // 2 способ - через сеттеры $customer = (new AtolOnline\Entities\Client()) ->setEmail('john@example.com') - ->setInn('+fasd3\q3fs_=nac9901 3928c-c') // 3399013928 + ->setInn('+fasd3\q3fs_=nac9901 3928c-c') ->setName('John Doe') - ->setPhone('+1/22/99*73s dsdas654 5s6'); // +122997365456 + ->setPhone('+1/22/99*73s dsdas654 5s6'); // либо комбинация этих способов ``` -Метод `setEmail()` проверяет входную строку на длину (до 64 символов) и валидность формата email. -Выбрасывает исключения: -* `AtolEmailTooLongException` (если слишком длинный email); -* `AtolEmailValidateException` (если email невалиден). - -Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр). -Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна). - -Метод `setName()` проверяет входную строку на длину (до 256 символов). -Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное имя). - -Метод `setPhone()` чистит входную строку от всех символов, кроме цифр и знака `+`, и проверяет длину (до 64 символов). -Выбрасывает исключение `AtolPhoneTooLongException` (если слишком длинный номер телефона). - -Конструктор может выбрасывать любое из указанных выше исключений, если в него передаются значения. - Получить установленные значения атрибутов можно через геттеры: ```php @@ -65,7 +49,7 @@ $customer->getName(); $customer->getPhone(); ``` -Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`: +Объект класса приводится к JSON-строке автоматически или принудительно: ```php echo $customer; diff --git a/docs/company.md b/docs/company.md index 912bd59..d13305e 100644 --- a/docs/company.md +++ b/docs/company.md @@ -22,16 +22,16 @@ $customer = new AtolOnline\Entities\Company(); Указать эти атрибуты можно двумя способами: ```php -// 1 способ - через конструктор +// 1 способ - через конструктор (все аргументы обязательны) $company = new AtolOnline\Entities\Company( + 'company@example.com' // email AtolOnline\Constants\SnoTypes::OSN, // тип СНО '5544332219', // номер ИНН 'https://v4.online.atol.ru', // адрес места расчётов - 'company@example.com' // email ); // 2 способ - через сеттеры -$company = (new AtolOnline\Entities\Company()) +$company ->setEmail('company@example.com') ->setInn('5544332219') ->setSno(AtolOnline\Constants\SnoTypes::USN_INCOME) @@ -40,19 +40,6 @@ $company = (new AtolOnline\Entities\Company()) // либо комбинация этих способов ``` -Метод `setEmail()` проверяет входную строку на длину (до 64 символов) и валидность формата email. -Выбрасывает исключения: -* `AtolEmailTooLongException` (если слишком длинный email); -* `AtolEmailValidateException` (если email невалиден). - -Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр). -Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна). - -Метод `setPaymentAddress()` проверяет длину (до 256 символов). -Выбрасывает исключение `AtolPaymentAddressTooLongException` (если слишком длинный адрес места расчётов). - -Конструктор может выбрасывать любое из указанных выше исключений, если в него передаются параметры. - Получить установленные значения параметров можно через геттеры: ```php @@ -62,7 +49,7 @@ $company->getPaymentAddress(); $company->getSno(); ``` -Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`: +Объект класса приводится к JSON-строке автоматически или принудительно: ```php echo $company; diff --git a/docs/monitoring.md b/docs/monitoring.md index 9addf52..6fafe5f 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -79,6 +79,19 @@ $kkt->networkErrorCode; // Код ошибки сети Сопоставления кодов ошибок и их описаний доступны в массиве `AtolOnline\Entities\Kkt::ERROR_CODES`. +Объект класса приводится к JSON-строке автоматически или принудительно: + +```php +echo $kkt; +$json_string = (string)$kkt; +``` + +Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`: + +```php +$json_array = $kkt->jsonSerialize(); +``` + ## Получение данных об одной из своих ККТ Для этого следует вызвать метод `AtolOnline\Api\KktMonitor::getOne()`, передав на вход серийный номер (`serialNumber`) diff --git a/src/Entities/Client.php b/src/Entities/Client.php index 063090b..6b13421 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -50,25 +50,26 @@ class Client extends Entity * Конструктор объекта покупателя * * @param string|null $name Наименование. Тег ФФД - 1227. - * @param string|null $phone Email. Тег ФФД - 1008. + * @param string|null $phone Email. Тег ФФД - 1008. * @param string|null $email Телефон покупателя. Тег ФФД - 1008. - * @param string|null $inn ИНН. Тег ФФД - 1228. - * @throws TooLongNameException Слишком длинное имя - * @throws TooLongPhoneException Слишком длинный телефон - * @throws TooLongEmailException Слишком длинный email - * @throws InvalidEmailException Невалидный email - * @throws InvalidInnLengthException Некорректная длина ИНН + * @param string|null $inn ИНН. Тег ФФД - 1228. + * @throws TooLongNameException + * @throws TooLongPhoneException + * @throws TooLongEmailException + * @throws InvalidEmailException + * @throws InvalidInnLengthException */ public function __construct( ?string $name = null, ?string $email = null, ?string $phone = null, ?string $inn = null - ) { - $name && $this->setName($name); - $email && $this->setEmail($email); - $phone && $this->setPhone($phone); - $inn && $this->setInn($inn); + ) + { + !is_null($name) && $this->setName($name); + !is_null($email) && $this->setEmail($email); + !is_null($phone) && $this->setPhone($phone); + !is_null($inn) && $this->setInn($inn); } /** @@ -92,7 +93,7 @@ class Client extends Entity * @return $this * @throws TooLongNameException */ - public function setName(?string $name): Client + public function setName(?string $name): self { if (is_string($name)) { $name = preg_replace('/[\n\r\t]/', '', trim($name)); @@ -157,7 +158,7 @@ class Client extends Entity * @return $this * @throws TooLongPhoneException */ - public function setPhone(?string $phone): Client + public function setPhone(?string $phone): self { if (is_string($phone)) { $phone = preg_replace('/[^\d]/', '', trim($phone)); diff --git a/src/Entities/Company.php b/src/Entities/Company.php index d5876d9..485bd0a 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -125,7 +125,7 @@ class Company extends Entity * @return $this * @throws InvalidSnoException */ - public function setSno(string $sno): Company + public function setSno(string $sno): self { $sno = trim($sno); if (empty($sno) || !in_array($sno, SnoTypes::toArray())) { @@ -188,7 +188,7 @@ class Company extends Entity * @throws TooLongPaymentAddressException * @throws InvalidPaymentAddressException */ - public function setPaymentAddress(string $payment_address): Company + public function setPaymentAddress(string $payment_address): self { $payment_address = trim($payment_address); if (empty($payment_address)) { From 3bf86674374b546531fc4426763ae602cf46a353 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 22 Nov 2021 14:51:10 +0800 Subject: [PATCH 16/85] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=B8=D1=81=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B9,=20=D0=BD=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82=D1=8B?= =?UTF-8?q?=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Описывать все слишком долго, TLDR: - упрощены корневые AtolException, {BasicTooLongException => TooLongException}, {BasicTooManyException => TooManyException} - InvalidSnoException заменён на InvalidEnumValueException - добавлены новые константы, общий порядок изменён в соответствии с порядком упоминания в документации, ссылки на которую тоже добавлены с указанием страниц Помимо этого, в enum-ах теперь должен быть предусмотрен метод getFfdTags() --- src/Api/AtolClient.php | 4 +- src/Constants/Constraints.php | 85 +++++++++++++++---- src/Entities/Client.php | 21 ++--- src/Entities/Company.php | 18 ++-- src/Entities/Document.php | 28 +++--- src/Entities/Item.php | 54 ++++++------ src/Enums/SnoTypes.php | 10 ++- src/Exceptions/AtolException.php | 30 ++----- src/Exceptions/AuthFailedException.php | 13 +-- src/Exceptions/BasicTooLongException.php | 40 --------- .../EmptyCorrectionInfoException.php | 11 ++- src/Exceptions/EmptyEmailException.php | 17 +--- src/Exceptions/EmptyLoginException.php | 10 +-- src/Exceptions/EmptyMonitorDataException.php | 7 +- src/Exceptions/EmptyPasswordException.php | 10 +-- .../InvalidCallbackUrlException.php | 7 +- .../InvalidDocumentTypeException.php | 38 --------- src/Exceptions/InvalidEmailException.php | 22 ++--- src/Exceptions/InvalidEnumValueException.php | 38 +++++++++ src/Exceptions/InvalidInnLengthException.php | 24 ++---- src/Exceptions/InvalidJsonException.php | 18 +--- .../InvalidPaymentAddressException.php | 24 ++---- src/Exceptions/InvalidSnoException.php | 45 ---------- src/Exceptions/InvalidUuidException.php | 10 +-- .../NotEnoughMonitorDataException.php | 12 +-- src/Exceptions/TooHighPriceException.php | 2 +- .../TooLongCallbackUrlException.php | 12 +-- src/Exceptions/TooLongCashierException.php | 20 ++--- ....php => TooLongClientContactException.php} | 26 ++---- src/Exceptions/TooLongClientNameException.php | 24 ++++++ src/Exceptions/TooLongEmailException.php | 21 ++--- src/Exceptions/TooLongException.php | 44 ++++++++++ ...ption.php => TooLongItemNameException.php} | 24 ++---- src/Exceptions/TooLongLoginException.php | 12 +-- src/Exceptions/TooLongPasswordException.php | 12 +-- .../TooLongPayingAgentOperationException.php | 24 ++++++ .../TooLongPaymentAddressException.php | 26 +++--- src/Exceptions/TooLongUnitException.php | 26 +++--- src/Exceptions/TooLongUserdataException.php | 20 ++--- ...ManyException.php => TooManyException.php} | 37 ++++---- src/Exceptions/TooManyItemsException.php | 12 +-- src/Exceptions/TooManyPaymentsException.php | 22 ++--- src/Exceptions/TooManyVatsException.php | 24 ++---- tests/BasicTestCase.php | 42 +++------ tests/ClientTest.php | 31 ++++--- tests/CompanyTest.php | 6 +- tests/ItemTest_todo.php | 16 ++-- tests/KktMonitorTest.php | 6 +- 48 files changed, 482 insertions(+), 603 deletions(-) delete mode 100644 src/Exceptions/BasicTooLongException.php delete mode 100644 src/Exceptions/InvalidDocumentTypeException.php create mode 100644 src/Exceptions/InvalidEnumValueException.php delete mode 100644 src/Exceptions/InvalidSnoException.php rename src/Exceptions/{TooLongPhoneException.php => TooLongClientContactException.php} (57%) create mode 100644 src/Exceptions/TooLongClientNameException.php create mode 100644 src/Exceptions/TooLongException.php rename src/Exceptions/{TooLongNameException.php => TooLongItemNameException.php} (58%) create mode 100644 src/Exceptions/TooLongPayingAgentOperationException.php rename src/Exceptions/{BasicTooManyException.php => TooManyException.php} (50%) diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index 51254e1..ca3bf78 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -161,7 +161,7 @@ abstract class AtolClient if (empty($login)) { throw new EmptyLoginException(); } elseif (mb_strlen($login) > Constraints::MAX_LENGTH_LOGIN) { - throw new TooLongLoginException($login, Constraints::MAX_LENGTH_LOGIN); + throw new TooLongLoginException($login); } $this->login = $login; return $this; @@ -190,7 +190,7 @@ abstract class AtolClient if (empty($password)) { throw new EmptyPasswordException(); } elseif (mb_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) { - throw new TooLongPasswordException($password, Constraints::MAX_LENGTH_PASSWORD); + throw new TooLongPasswordException($password); } $this->password = $password; return $this; diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index 5ca70b7..44ad74a 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -36,40 +36,89 @@ final class Constraints */ const MAX_LENGTH_PASSWORD = 100; - /** - * Максимальная длина имени покупателя - */ - const MAX_LENGTH_CLIENT_NAME = 256; - - /** - * Максимальная длина телефона покупателя - */ - const MAX_LENGTH_CLIENT_PHONE = 64; - /** * Максимальная длина адреса места расчётов */ const MAX_LENGTH_PAYMENT_ADDRESS = 256; - + /** - * Максимальная длина имени кассира + * Максимальная длина наименования покупателя (1227) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ - const MAX_LENGTH_CASHIER_NAME = 64; - + const MAX_LENGTH_CLIENT_NAME = 256; + /** - * Максимальная длина наименования предмета расчётов + * Максимальная длина наименования предмета расчёта (1030) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_LENGTH_ITEM_NAME = 128; - + + /** + * Максимальная цена за единицу предмета расчёта (1079) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 + */ + const MAX_COUNT_ITEM_PRICE = 42949672.95; + + /** + * Максимальное количество (вес) единицы предмета расчёта (1023) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 + */ + const MAX_COUNT_ITEM_QUANTITY = 99999.999; + + /** + * Максимальная стоимость всех предметов расчёта в документе прихода, расхода, + * возврата прихода, возврата расхода (1043) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 + */ + const MAX_COUNT_ITEM_SUM = 42949672.95; + + /** + * Максимальная длина телефона или email покупателя (1008) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 + */ + const MAX_LENGTH_CLIENT_CONTACT = 64; + + /** + * Длина операции для платёжного агента (1044) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19 + */ + const MAX_LENGTH_PAYING_AGENT_OPERATION = 24; + + /** + * Максимальное количество предметов расчёта в документе прихода, расхода, возврата прихода, возврата расхода + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 + */ + const MAX_COUNT_DOC_ITEMS = 100; + /** * Максимальная длина единицы измерения предмета расчётов + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_LENGTH_MEASUREMENT_UNIT = 16; - + /** - * Максимальная длина пользовательских данных для предмета расчётов + * Максимальная длина пользовательских данных для предмета расчётов (1191) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29 */ const MAX_LENGTH_USER_DATA = 64; + + /** + * Максимальное количество платежей в любом документе + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30 и 35 + */ + const MAX_COUNT_DOC_PAYMENTS = 10; + + /** + * Максимальное количество ставок НДС в любом документе + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 31 и 36 + */ + const MAX_COUNT_DOC_VATS = 6; + + /** + * Максимальная длина имени кассира (1021) + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32 + */ + const MAX_LENGTH_CASHIER_NAME = 64; /** * Регулярное выражание для валидации строки ИНН diff --git a/src/Entities/Client.php b/src/Entities/Client.php index 6b13421..89ca0fe 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -15,9 +15,10 @@ use AtolOnline\{ Constants\Constraints, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, + Exceptions\TooLongClientContactException, + Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException, - Exceptions\TooLongNameException, - Exceptions\TooLongPhoneException}; + Exceptions\TooLongItemNameException}; /** * Класс Client, описывающий сущность покупателя @@ -53,8 +54,8 @@ class Client extends Entity * @param string|null $phone Email. Тег ФФД - 1008. * @param string|null $email Телефон покупателя. Тег ФФД - 1008. * @param string|null $inn ИНН. Тег ФФД - 1228. - * @throws TooLongNameException - * @throws TooLongPhoneException + * @throws TooLongItemNameException + * @throws TooLongClientContactException * @throws TooLongEmailException * @throws InvalidEmailException * @throws InvalidInnLengthException @@ -91,14 +92,14 @@ class Client extends Entity * * @param string|null $name * @return $this - * @throws TooLongNameException + * @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 TooLongNameException($name, Constraints::MAX_LENGTH_CLIENT_NAME); + throw new TooLongClientNameException($name); } } $this->name = empty($name) ? null : $name; @@ -128,7 +129,7 @@ class Client extends Entity if (is_string($email)) { $email = preg_replace('/[\n\r\t]/', '', trim($email)); if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) { - throw new TooLongEmailException($email, Constraints::MAX_LENGTH_EMAIL); + throw new TooLongEmailException($email); } elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { throw new InvalidEmailException($email); } @@ -156,14 +157,14 @@ class Client extends Entity * * @param string|null $phone Номер телефона * @return $this - * @throws TooLongPhoneException + * @throws TooLongClientContactException */ public function setPhone(?string $phone): self { if (is_string($phone)) { $phone = preg_replace('/[^\d]/', '', trim($phone)); - if (mb_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_PHONE) { - throw new TooLongPhoneException($phone, Constraints::MAX_LENGTH_CLIENT_PHONE); + if (mb_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_CONTACT) { + throw new TooLongClientContactException($phone); } } $this->phone = empty($phone) ? null : "+$phone"; diff --git a/src/Entities/Company.php b/src/Entities/Company.php index 485bd0a..df3f8f2 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -15,9 +15,9 @@ use AtolOnline\{ Constants\Constraints, Enums\SnoTypes, Exceptions\InvalidEmailException, + Exceptions\InvalidEnumValueException, Exceptions\InvalidInnLengthException, Exceptions\InvalidPaymentAddressException, - Exceptions\InvalidSnoException, Exceptions\TooLongEmailException, Exceptions\TooLongPaymentAddressException}; @@ -58,7 +58,7 @@ class Company extends Entity * @throws InvalidEmailException * @throws InvalidInnLengthException * @throws InvalidPaymentAddressException - * @throws InvalidSnoException + * @throws InvalidEnumValueException * @throws TooLongEmailException * @throws TooLongPaymentAddressException */ @@ -96,7 +96,7 @@ class Company extends Entity { $email = preg_replace('/[\n\r\t]/', '', trim($email)); if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) { - throw new TooLongEmailException($email, Constraints::MAX_LENGTH_EMAIL); + throw new TooLongEmailException($email); } elseif (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) { throw new InvalidEmailException($email); } @@ -123,14 +123,12 @@ class Company extends Entity * * @param string $sno * @return $this - * @throws InvalidSnoException + * @throws InvalidEnumValueException */ public function setSno(string $sno): self { $sno = trim($sno); - if (empty($sno) || !in_array($sno, SnoTypes::toArray())) { - throw new InvalidSnoException($sno); - } + SnoTypes::isValid($sno); $this->sno = $sno; return $this; } @@ -194,7 +192,7 @@ class Company extends Entity if (empty($payment_address)) { throw new InvalidPaymentAddressException(); } elseif (mb_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) { - throw new TooLongPaymentAddressException($payment_address, Constraints::MAX_LENGTH_PAYMENT_ADDRESS); + throw new TooLongPaymentAddressException($payment_address); } $this->payment_address = $payment_address; return $this; @@ -203,7 +201,7 @@ class Company extends Entity /** * @inheritDoc * @throws InvalidEmailException - * @throws InvalidSnoException + * @throws InvalidEnumValueException * @throws InvalidInnLengthException * @throws InvalidPaymentAddressException */ @@ -215,7 +213,7 @@ class Company extends Entity : throw new InvalidEmailException(), 'sno' => $this->sno ? $this->getSno() - : throw new InvalidSnoException(), + : throw new InvalidEnumValueException(SnoTypes::class, 'null'), 'inn' => $this->inn ? $this->getInn() : throw new InvalidInnLengthException(), diff --git a/src/Entities/Document.php b/src/Entities/Document.php index af72077..4bc4e7c 100644 --- a/src/Entities/Document.php +++ b/src/Entities/Document.php @@ -13,18 +13,18 @@ namespace AtolOnline\Entities; use AtolOnline\Constants\Constraints; use AtolOnline\Exceptions\AtolException; -use AtolOnline\Exceptions\BasicTooManyException; use AtolOnline\Exceptions\InvalidEmailException; use AtolOnline\Exceptions\InvalidInnLengthException; use AtolOnline\Exceptions\InvalidJsonException; use AtolOnline\Exceptions\TooHighPriceException; use AtolOnline\Exceptions\TooLongCashierException; +use AtolOnline\Exceptions\TooLongClientContactException; use AtolOnline\Exceptions\TooLongEmailException; -use AtolOnline\Exceptions\TooLongNameException; +use AtolOnline\Exceptions\TooLongItemNameException; use AtolOnline\Exceptions\TooLongPaymentAddressException; -use AtolOnline\Exceptions\TooLongPhoneException; use AtolOnline\Exceptions\TooLongUnitException; use AtolOnline\Exceptions\TooLongUserdataException; +use AtolOnline\Exceptions\TooManyException; use AtolOnline\Exceptions\TooManyItemsException; use AtolOnline\Exceptions\TooManyPaymentsException; use AtolOnline\Exceptions\TooManyVatsException; @@ -66,7 +66,7 @@ class Document extends Entity * @var float Итоговая сумма чека. Тег ФФД - 1020. */ protected float $total = 0; - + /** * @var string ФИО кассира. Тег ФФД - 1021. */ @@ -76,7 +76,7 @@ class Document extends Entity * @var CorrectionInfo Данные коррекции */ protected CorrectionInfo $correction_info; - + /** * Document constructor. */ @@ -211,7 +211,7 @@ class Document extends Entity $this->items->set($items); return $this; } - + /** * Возвращает заданного клиента (покупателя) * @@ -221,7 +221,7 @@ class Document extends Entity { return $this->client; } - + /** * Устанавливает клиента (покупателя) * @@ -233,7 +233,7 @@ class Document extends Entity $this->client = $client; return $this; } - + /** * Возвращает заданную компанию (продавца) * @@ -243,7 +243,7 @@ class Document extends Entity { return $this->company; } - + /** * Устанавливает компанию (продавца) * @@ -255,7 +255,7 @@ class Document extends Entity $this->company = $company; return $this; } - + /** * Возвращает ФИО кассира. Тег ФФД - 1021. * @@ -323,7 +323,7 @@ class Document extends Entity } return $this->total = round($sum, 2); } - + /** * Возвращает итоговую сумму чека. Тег ФФД - 1020. * @@ -344,11 +344,11 @@ class Document extends Entity * @throws AtolException * @throws InvalidInnLengthException * @throws InvalidJsonException - * @throws TooLongNameException + * @throws TooLongItemNameException * @throws TooLongPaymentAddressException - * @throws TooLongPhoneException + * @throws TooLongClientContactException * @throws TooHighPriceException - * @throws BasicTooManyException + * @throws TooManyException * @throws TooManyItemsException * @throws TooManyPaymentsException * @throws TooLongUnitException diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 64e11f3..1d0c95b 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -13,11 +13,11 @@ namespace AtolOnline\Entities; use AtolOnline\{ Constants\Constraints, - Exceptions\BasicTooManyException, Exceptions\TooHighPriceException, - Exceptions\TooLongNameException, + Exceptions\TooLongItemNameException, Exceptions\TooLongUnitException, - Exceptions\TooLongUserdataException}; + Exceptions\TooLongUserdataException, + Exceptions\TooManyException}; /** * Предмет расчёта (товар, услуга) @@ -30,22 +30,22 @@ class Item extends Entity * @var string Наименование. Тег ФФД - 1030. */ protected string $name; - + /** * @var int Цена в копейках (с учётом скидок и наценок). Тег ФФД - 1079. */ protected int $price = 0; - + /** * @var float Количество, вес. Тег ФФД - 1023. */ protected float $quantity = 0.0; - + /** * @var float Сумма в копейках. Тег ФФД - 1043. */ protected float $sum = 0; - + /** * @var string Единица измерения количества. Тег ФФД - 1197. */ @@ -55,17 +55,17 @@ class Item extends Entity * @var Vat|null Ставка НДС */ protected ?Vat $vat; - + /** * @var string Признак способа расчёта. Тег ФФД - 1214. */ protected string $payment_method; - + /** * @var string Признак объекта расчёта. Тег ФФД - 1212. */ protected string $payment_object; - + /** * @var string Дополнительный реквизит. Тег ФФД - 1191. */ @@ -81,9 +81,9 @@ class Item extends Entity * @param string|null $vat_type Ставка НДС * @param string|null $payment_object Признак * @param string|null $payment_method Способ расчёта - * @throws TooLongNameException Слишком длинное наименование + * @throws TooLongItemNameException Слишком длинное наименование * @throws TooHighPriceException Слишком высокая цена за одну единицу - * @throws BasicTooManyException Слишком большое количество + * @throws TooManyException Слишком большое количество * @throws TooLongUnitException Слишком длинное название единицы измерения */ public function __construct( @@ -117,7 +117,7 @@ class Item extends Entity $this->setPaymentMethod($payment_method); } } - + /** * Возвращает наименование. Тег ФФД - 1030. * @@ -133,18 +133,18 @@ class Item extends Entity * * @param string $name Наименование * @return $this - * @throws TooLongNameException Слишком длинное имя/наименование + * @throws TooLongItemNameException Слишком длинное имя/наименование */ public function setName(string $name): self { $name = trim($name); if (mb_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) { - throw new TooLongNameException($name, Constraints::MAX_LENGTH_ITEM_NAME); + throw new TooLongItemNameException($name, Constraints::MAX_LENGTH_ITEM_NAME); } $this->name = $name; return $this; } - + /** * Возвращает цену в рублях. Тег ФФД - 1079. * @@ -171,7 +171,7 @@ class Item extends Entity $this->calcSum(); return $this; } - + /** * Возвращает количество. Тег ФФД - 1023. * @@ -188,7 +188,7 @@ class Item extends Entity * @param float $quantity Количество * @param string|null $measurement_unit Единица измерения количества * @return $this - * @throws BasicTooManyException Слишком большое количество + * @throws TooManyException Слишком большое количество * @throws TooHighPriceException Слишком высокая общая стоимость * @throws TooLongUnitException Слишком длинное название единицы измерения */ @@ -196,7 +196,7 @@ class Item extends Entity { $quantity = round($quantity, 3); if ($quantity > 99999.999) { - throw new BasicTooManyException($quantity, 99999.999); + throw new TooManyException($quantity, 99999.999); } $this->quantity = $quantity; $this->calcSum(); @@ -205,7 +205,7 @@ class Item extends Entity } return $this; } - + /** * Возвращает заданную единицу измерения количества. Тег ФФД - 1197. * @@ -232,7 +232,7 @@ class Item extends Entity $this->measurement_unit = $measurement_unit; return $this; } - + /** * Возвращает признак способа оплаты. Тег ФФД - 1214. * @@ -242,7 +242,7 @@ class Item extends Entity { return $this->payment_method; } - + /** * Устанавливает признак способа оплаты. Тег ФФД - 1214. * @@ -255,7 +255,7 @@ class Item extends Entity $this->payment_method = trim($payment_method); return $this; } - + /** * Возвращает признак предмета расчёта. Тег ФФД - 1212. * @@ -265,7 +265,7 @@ class Item extends Entity { return $this->payment_object; } - + /** * Устанавливает признак предмета расчёта. Тег ФФД - 1212. * @@ -308,7 +308,7 @@ class Item extends Entity $this->calcSum(); return $this; } - + /** * Возвращает дополнительный реквизит. Тег ФФД - 1191. * @@ -335,7 +335,7 @@ class Item extends Entity $this->user_data = $user_data; return $this; } - + /** * Возвращает стоимость. Тег ФФД - 1043. * @@ -364,7 +364,7 @@ class Item extends Entity } return $this->getSum(); } - + /** * @inheritDoc */ diff --git a/src/Enums/SnoTypes.php b/src/Enums/SnoTypes.php index 0892fc6..0e9e851 100644 --- a/src/Enums/SnoTypes.php +++ b/src/Enums/SnoTypes.php @@ -11,7 +11,7 @@ declare(strict_types = 1); namespace AtolOnline\Enums; -use MyCLabs\Enum\Enum; +use AtolOnline\Enum; /** * Константы, определяющие типы налогообложения @@ -49,4 +49,12 @@ final class SnoTypes extends Enum * Патентная СН */ const PATENT = 'patent'; + + /** + * @return int[] Возвращает массив тегов ФФД + */ + public static function getFfdTags(): array + { + return [1055]; + } } \ No newline at end of file diff --git a/src/Exceptions/AtolException.php b/src/Exceptions/AtolException.php index b548ab6..d3e9237 100644 --- a/src/Exceptions/AtolException.php +++ b/src/Exceptions/AtolException.php @@ -12,7 +12,6 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; use Exception; -use Throwable; /** * Исключение, возникающее при работе с АТОЛ Онлайн @@ -25,28 +24,15 @@ class AtolException extends Exception protected array $ffd_tags = []; /** - * AtolException constructor. + * Конструктор * - * @param string $message - * @param int $code - * @param Throwable|null $previous + * @param string $message Сообщение + * @param int[] $ffd_tags Переопредление тегов ФФД */ - public function __construct($message = "", $code = 0, Throwable $previous = null) + public function __construct(string $message = '', array $ffd_tags = []) { - $message = $message ?: $this->message; - if ($this->getFfdTags()) { - $message .= ' [FFD tags: '.implode(', ', $this->getFfdTags()).']'; - } - parent::__construct($message, $code, $previous); + parent::__construct( + ($message ?: $this->message) . ' [Теги ФФД: ' . implode(', ', $ffd_tags ?: $this->ffd_tags) . ']' + ); } - - /** - * Возвращает теги ФФД, с которыми связано исключение - * - * @return array - */ - protected function getFfdTags(): array - { - return $this->ffd_tags; - } -} \ No newline at end of file +} diff --git a/src/Exceptions/AuthFailedException.php b/src/Exceptions/AuthFailedException.php index e213f6b..0d379b0 100644 --- a/src/Exceptions/AuthFailedException.php +++ b/src/Exceptions/AuthFailedException.php @@ -13,7 +13,6 @@ namespace AtolOnline\Exceptions; use AtolOnline\Api\KktResponse; use Exception; -use Throwable; /** * Исключение, возникающее при неудачной авторизации @@ -25,17 +24,9 @@ class AuthFailedException extends Exception * * @param KktResponse $response * @param string $message - * @param int $code - * @param Throwable|null $previous */ - public function __construct(KktResponse $response, $message = "", $code = 0, Throwable $previous = null) + public function __construct(KktResponse $response, string $message = '') { - $message = $response->isValid() - ? $message - : '[' . $response->error->code . '] ' . $response->error->text . - '. ERROR_ID: ' . $response->error->error_id . - '. TYPE: ' . $response->error->type; - $code = $response->isValid() ? $code : $response->error->code; - parent::__construct($message, $code, $previous); + parent::__construct(($message ?: 'Ошибка авторизации: ') . ': ' . $response); } } diff --git a/src/Exceptions/BasicTooLongException.php b/src/Exceptions/BasicTooLongException.php deleted file mode 100644 index d27905b..0000000 --- a/src/Exceptions/BasicTooLongException.php +++ /dev/null @@ -1,40 +0,0 @@ -message . ' (max length - ' . $max . ', actual length - ' . - mb_strlen($string), $code, $previous); - } -} \ No newline at end of file diff --git a/src/Exceptions/EmptyCorrectionInfoException.php b/src/Exceptions/EmptyCorrectionInfoException.php index f0b2f1f..172108a 100644 --- a/src/Exceptions/EmptyCorrectionInfoException.php +++ b/src/Exceptions/EmptyCorrectionInfoException.php @@ -11,13 +11,12 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Entities\CorrectionInfo; + /** - * Исключение, возникающее при попытке зарегистрировать документ без данных коррекции + * Исключение, возникающее при попытке зарегистрировать документ коррекции без соотв. данных */ class EmptyCorrectionInfoException extends AtolException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Document must have correction info'; -} \ No newline at end of file + protected $message = 'Документ должен содержать объект ' . CorrectionInfo::class; +} diff --git a/src/Exceptions/EmptyEmailException.php b/src/Exceptions/EmptyEmailException.php index 52ce4ad..bb4200b 100644 --- a/src/Exceptions/EmptyEmailException.php +++ b/src/Exceptions/EmptyEmailException.php @@ -13,19 +13,10 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать пустой email + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ class EmptyEmailException extends AtolException { - /** - * @inheritDoc - */ - protected array $ffd_tags = [ - 1008, - 1117, - ]; - - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Email cannot be empty'; -} \ No newline at end of file + protected $message = 'Email не может быть пустым'; + protected array $ffd_tags = [1008, 1117]; +} diff --git a/src/Exceptions/EmptyLoginException.php b/src/Exceptions/EmptyLoginException.php index fbd4b5e..5e78966 100644 --- a/src/Exceptions/EmptyLoginException.php +++ b/src/Exceptions/EmptyLoginException.php @@ -12,12 +12,10 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; /** - * Исключение, возникающее при попытке указать пустой логин ККТ + * Исключение, возникающее при попытке указать пустой логин + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12 */ class EmptyLoginException extends AtolException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'KKT login cannot be empty'; -} \ No newline at end of file + protected $message = 'Логин не может быть пустым'; +} diff --git a/src/Exceptions/EmptyMonitorDataException.php b/src/Exceptions/EmptyMonitorDataException.php index 7aad529..32902d6 100644 --- a/src/Exceptions/EmptyMonitorDataException.php +++ b/src/Exceptions/EmptyMonitorDataException.php @@ -16,8 +16,5 @@ namespace AtolOnline\Exceptions; */ class EmptyMonitorDataException extends AtolException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Cannot create KKT entity without data from monitor'; -} \ No newline at end of file + protected $message = 'Не возможно создать объект ККт без данных от мониторинга'; +} diff --git a/src/Exceptions/EmptyPasswordException.php b/src/Exceptions/EmptyPasswordException.php index d3370b8..76f4a9d 100644 --- a/src/Exceptions/EmptyPasswordException.php +++ b/src/Exceptions/EmptyPasswordException.php @@ -12,12 +12,10 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; /** - * Исключение, возникающее при попытке указать пустой пароль ККТ + * Исключение, возникающее при попытке указать пустой пароль + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12 */ class EmptyPasswordException extends AtolException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'KKT password cannot be empty'; -} \ No newline at end of file + protected $message = 'Пароль не может быть пустым'; +} diff --git a/src/Exceptions/InvalidCallbackUrlException.php b/src/Exceptions/InvalidCallbackUrlException.php index e8240f3..daeb446 100644 --- a/src/Exceptions/InvalidCallbackUrlException.php +++ b/src/Exceptions/InvalidCallbackUrlException.php @@ -16,8 +16,5 @@ namespace AtolOnline\Exceptions; */ class InvalidCallbackUrlException extends AtolException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Invalid callback URL'; -} \ No newline at end of file + protected $message = 'Невалидный callback_url'; +} diff --git a/src/Exceptions/InvalidDocumentTypeException.php b/src/Exceptions/InvalidDocumentTypeException.php deleted file mode 100644 index c7aca45..0000000 --- a/src/Exceptions/InvalidDocumentTypeException.php +++ /dev/null @@ -1,38 +0,0 @@ -message . implode(', ', $props_diff), $code, $previous); + parent::__construct($message ?: $this->message . implode(', ', $props_diff)); } -} \ No newline at end of file +} diff --git a/src/Exceptions/TooHighPriceException.php b/src/Exceptions/TooHighPriceException.php index 1f523bf..0760cc0 100644 --- a/src/Exceptions/TooHighPriceException.php +++ b/src/Exceptions/TooHighPriceException.php @@ -14,7 +14,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать слишком высокую цену (сумму) */ -class TooHighPriceException extends BasicTooManyException +class TooHighPriceException extends TooManyException { /** * @inheritDoc diff --git a/src/Exceptions/TooLongCallbackUrlException.php b/src/Exceptions/TooLongCallbackUrlException.php index ed08899..efb1775 100644 --- a/src/Exceptions/TooLongCallbackUrlException.php +++ b/src/Exceptions/TooLongCallbackUrlException.php @@ -11,13 +11,13 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке указать слишком длинный callback_url */ -class TooLongCallbackUrlException extends BasicTooLongException +class TooLongCallbackUrlException extends TooLongException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Callback URL is too long'; -} \ No newline at end of file + protected $message = 'Слишком длинный адрес колбека'; + protected int $max = Constraints::MAX_LENGTH_CALLBACK_URL; +} diff --git a/src/Exceptions/TooLongCashierException.php b/src/Exceptions/TooLongCashierException.php index 056ffd7..762d1a9 100644 --- a/src/Exceptions/TooLongCashierException.php +++ b/src/Exceptions/TooLongCashierException.php @@ -11,20 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке указать слишком длинное имя кассира */ -class TooLongCashierException extends BasicTooLongException +class TooLongCashierException extends TooLongException { - /** - * @inheritDoc - */ - protected array $ffd_tags = [ - 1021, - ]; - - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Cashier name is too long'; -} \ No newline at end of file + protected $message = 'Слишком длинное имя кассира'; + protected int $max = Constraints::MAX_LENGTH_CASHIER_NAME; + protected array $ffd_tags = [1021]; +} diff --git a/src/Exceptions/TooLongPhoneException.php b/src/Exceptions/TooLongClientContactException.php similarity index 57% rename from src/Exceptions/TooLongPhoneException.php rename to src/Exceptions/TooLongClientContactException.php index 9cedc3e..183e34e 100644 --- a/src/Exceptions/TooLongPhoneException.php +++ b/src/Exceptions/TooLongClientContactException.php @@ -11,24 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** - * Исключение, возникающее при попытке указать слишком длинный телефон + * Исключение, возникающее при попытке указать слишком длинный телефон или email покупателя */ -class TooLongPhoneException extends BasicTooLongException +class TooLongClientContactException extends TooLongException { - /** - * @inheritDoc - */ - protected array $ffd_tags = [ - 1008, - 1073, - 1074, - 1075, - 1171, - ]; - - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Phone is too long'; -} \ No newline at end of file + protected $message = 'Cлишком длинный телефон или email покупателя'; + protected int $max = Constraints::MAX_LENGTH_CLIENT_CONTACT; + protected array $ffd_tags = [1008]; +} diff --git a/src/Exceptions/TooLongClientNameException.php b/src/Exceptions/TooLongClientNameException.php new file mode 100644 index 0000000..30bf22e --- /dev/null +++ b/src/Exceptions/TooLongClientNameException.php @@ -0,0 +1,24 @@ +message) . ': '. $value; + if ($max > 0 || $this->max > 0) { + $message .= ' (макс. = ' . ($max ?? $this->max) . ', фактически = ' . mb_strlen($value) . ')'; + } + parent::__construct($message); + } +} diff --git a/src/Exceptions/TooLongNameException.php b/src/Exceptions/TooLongItemNameException.php similarity index 58% rename from src/Exceptions/TooLongNameException.php rename to src/Exceptions/TooLongItemNameException.php index d26a32c..eb3a3b7 100644 --- a/src/Exceptions/TooLongNameException.php +++ b/src/Exceptions/TooLongItemNameException.php @@ -11,24 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке указать слишком длинное имя */ -class TooLongNameException extends BasicTooLongException +class TooLongItemNameException extends TooLongException { - /** - * @inheritDoc - */ - protected array $ffd_tags = [ - 1026, - 1030, - 1085, - 1225, - 1227, - ]; - - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Name is too long'; -} \ No newline at end of file + protected $message = 'Слишком длинное наименование предмета расчёта'; + protected int $max = Constraints::MAX_LENGTH_ITEM_NAME; + protected array $ffd_tags = [1030]; +} diff --git a/src/Exceptions/TooLongLoginException.php b/src/Exceptions/TooLongLoginException.php index 51170e9..a9fe073 100644 --- a/src/Exceptions/TooLongLoginException.php +++ b/src/Exceptions/TooLongLoginException.php @@ -11,13 +11,13 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке указать слишком длинный логин ККТ */ -class TooLongLoginException extends BasicTooLongException +class TooLongLoginException extends TooLongException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'KKT login is too long'; -} \ No newline at end of file + protected $message = 'Слишком длинный логин'; + protected int $max = Constraints::MAX_LENGTH_LOGIN; +} diff --git a/src/Exceptions/TooLongPasswordException.php b/src/Exceptions/TooLongPasswordException.php index c9fb51e..c21477a 100644 --- a/src/Exceptions/TooLongPasswordException.php +++ b/src/Exceptions/TooLongPasswordException.php @@ -11,13 +11,13 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке указать слишком длинный пароль ККТ */ -class TooLongPasswordException extends BasicTooLongException +class TooLongPasswordException extends TooLongException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'KKT password is too long'; -} \ No newline at end of file + protected $message = 'Слишком длинный пароль'; + protected int $max = Constraints::MAX_LENGTH_PASSWORD; +} diff --git a/src/Exceptions/TooLongPayingAgentOperationException.php b/src/Exceptions/TooLongPayingAgentOperationException.php new file mode 100644 index 0000000..c5079ea --- /dev/null +++ b/src/Exceptions/TooLongPayingAgentOperationException.php @@ -0,0 +1,24 @@ +message . ' (max - ' . $max . ', actual - ' . $quantity . ')', - $code, - $previous - ); + $message = ($message ?: $this->message) . ': ' . $value; + if ($max > 0 || $this->max > 0) { + $message .= ' (макс. = ' . ($max ?? $this->max) . ', фактически = ' . $value . ')'; + } + parent::__construct($message); } -} \ No newline at end of file +} diff --git a/src/Exceptions/TooManyItemsException.php b/src/Exceptions/TooManyItemsException.php index 22ce54b..f154a73 100644 --- a/src/Exceptions/TooManyItemsException.php +++ b/src/Exceptions/TooManyItemsException.php @@ -11,13 +11,13 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке добавить слишком много предметов расчёта в массив */ -class TooManyItemsException extends BasicTooManyException +class TooManyItemsException extends TooManyException { - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Too many items'; -} \ No newline at end of file + protected $message = 'Слишком много предметов расчёта в документе'; + protected int $max = Constraints::MAX_COUNT_DOC_ITEMS; +} diff --git a/src/Exceptions/TooManyPaymentsException.php b/src/Exceptions/TooManyPaymentsException.php index 48f5426..0e7e532 100644 --- a/src/Exceptions/TooManyPaymentsException.php +++ b/src/Exceptions/TooManyPaymentsException.php @@ -11,23 +11,13 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке добавить слишком много платежей в массив */ -class TooManyPaymentsException extends BasicTooManyException +class TooManyPaymentsException extends TooManyException { - /** - * @inheritDoc - */ - protected array $ffd_tags = [ - 1031, - 1081, - 1215, - 1217, - ]; - - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Too many payments'; -} \ No newline at end of file + protected $message = 'Слишком много платежей в документе'; + protected int $max = Constraints::MAX_COUNT_DOC_PAYMENTS; +} diff --git a/src/Exceptions/TooManyVatsException.php b/src/Exceptions/TooManyVatsException.php index 7ee4200..b39b38e 100644 --- a/src/Exceptions/TooManyVatsException.php +++ b/src/Exceptions/TooManyVatsException.php @@ -11,25 +11,13 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Constraints; + /** * Исключение, возникающее при попытке добавить слишком много ставок НДС в массив */ -class TooManyVatsException extends BasicTooManyException +class TooManyVatsException extends TooManyException { - /** - * @inheritDoc - */ - protected array $ffd_tags = [ - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - ]; - - /** - * @var string Сообщение об ошибке - */ - protected $message = 'Too many vats'; -} \ No newline at end of file + protected $message = 'Слишком много ставок НДС в документе'; + protected int $max = Constraints::MAX_COUNT_DOC_VATS; +} diff --git a/tests/BasicTestCase.php b/tests/BasicTestCase.php index 4bd5c23..aa63b9f 100644 --- a/tests/BasicTestCase.php +++ b/tests/BasicTestCase.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace AtolOnlineTests; use AtolOnline\Entities\Entity; +use AtolOnline\Helpers; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Collection; @@ -89,57 +90,40 @@ class BasicTestCase extends TestCase */ public function assertIsSameClass(object|string $expected, object|string $actual): void { - $this->assertEquals( - is_object($expected) ? $expected::class : $expected, - is_object($actual) ? $actual::class : $actual - ); + $this->assertTrue(Helpers::isSameClass($expected, $actual)); } /** * Тестирует наследование класса (объекта) от указанных классов * + * @param object|string $class * @param string[] $parents - * @param object|string $actual */ - public function assertExtendsClasses(array $parents, object|string $actual): void + public function assertExtendsClasses(object|string $class, array $parents): void { - $this->checkClassesIntersection($parents, $actual, 'class_parents'); + $this->assertTrue(Helpers::checkExtendsClasses($class, $parents)); } /** * Тестирует имплементацию классом (объектом) указанных интерфейсов * - * @param string[] $parents - * @param object|string $actual + * @param object|string $class + * @param string[] $interfaces */ - public function assertImplementsInterfaces(array $parents, object|string $actual): void + public function assertImplementsInterfaces(object|string $class, array $interfaces): void { - $this->checkClassesIntersection($parents, $actual, 'class_implements'); + $this->assertTrue(Helpers::checkImplementsInterfaces($class, $interfaces)); } /** * Тестирует использование классом (объектом) указанных трейтов * - * @param string[] $parents - * @param object|string $actual + * @param object|string $class + * @param string[] $traits */ - public function assertUsesTraits(array $parents, object|string $actual): void + public function assertUsesTraits(object|string $class, array $traits): void { - $this->checkClassesIntersection($parents, $actual, 'class_uses'); - } - - /** - * Проверяет пересечение классов указанной функцией SPL - * - * @param object|string $class Класс для проверки на вхождение, или объект, класс коего нужно проверить - * @param array $classes Массив классов, вхождение в который нужно проверить - * @param string $function class_parents|class_implements|class_uses - */ - protected function checkClassesIntersection(array $classes, object|string $class, string $function): void - { - $actual_classes = is_object($class) ? $function($class) : [$class::class]; - $this->assertIsArray($actual_classes); - $this->assertNotEmpty(array_intersect($classes, $actual_classes)); + $this->assertTrue(Helpers::checkUsesTraits($traits, $class)); } /** diff --git a/tests/ClientTest.php b/tests/ClientTest.php index be2ce7a..0442646 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -13,11 +13,11 @@ use AtolOnline\{ Entities\Client, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, + Exceptions\TooLongClientContactException, + Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException, - Exceptions\TooLongNameException, - Exceptions\TooLongPhoneException, - Helpers -}; + Exceptions\TooLongItemNameException, + Helpers}; /** * Набор тестов для проверки работы класс покупателя @@ -135,7 +135,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setName * @covers \AtolOnline\Entities\Client::getName - * @throws TooLongNameException + * @throws TooLongClientNameException */ public function testNullableNames(mixed $name): void { @@ -149,7 +149,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setName * @covers \AtolOnline\Entities\Client::getName - * @throws TooLongNameException + * @throws TooLongItemNameException */ public function testValidName(): void { @@ -163,12 +163,11 @@ class ClientTest extends BasicTestCase * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setName - * @covers \AtolOnline\Exceptions\TooLongNameException - * @throws TooLongNameException + * @covers \AtolOnline\Exceptions\TooLongClientNameException */ public function testInvalidName(): void { - $this->expectException(TooLongNameException::class); + $this->expectException(TooLongClientNameException::class); (new Client())->setName(Helpers::randomStr(400)); } @@ -182,7 +181,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Entities\Client::getPhone - * @throws TooLongPhoneException + * @throws TooLongClientContactException */ public function testNullablePhones(mixed $phone): void { @@ -197,7 +196,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Entities\Client::getPhone - * @throws TooLongPhoneException + * @throws TooLongClientContactException */ public function testValidPhone(string $input, string $output): void { @@ -210,12 +209,12 @@ class ClientTest extends BasicTestCase * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone - * @covers \AtolOnline\Exceptions\TooLongPhoneException - * @throws TooLongPhoneException + * @covers \AtolOnline\Exceptions\TooLongClientContactException + * @throws TooLongClientContactException */ - public function testInvalidPhone(): void + public function testTooLongClientPhone(): void { - $this->expectException(TooLongPhoneException::class); + $this->expectException(TooLongClientContactException::class); (new Client())->setPhone('99999999999999999999999999999999999999999999999999999999999999999999999999'); } @@ -315,4 +314,4 @@ class ClientTest extends BasicTestCase $this->expectException(InvalidInnLengthException::class); (new Client())->setInn('1234567890123'); } -} \ No newline at end of file +} diff --git a/tests/CompanyTest.php b/tests/CompanyTest.php index 82961af..93fa09c 100644 --- a/tests/CompanyTest.php +++ b/tests/CompanyTest.php @@ -13,9 +13,9 @@ use AtolOnline\{ Entities\Company, Enums\SnoTypes, Exceptions\InvalidEmailException, + Exceptions\InvalidEnumValueException, Exceptions\InvalidInnLengthException, Exceptions\InvalidPaymentAddressException, - Exceptions\InvalidSnoException, Exceptions\TooLongEmailException, Exceptions\TooLongPaymentAddressException, Helpers}; @@ -85,11 +85,11 @@ class CompanyTest extends BasicTestCase * * @covers \AtolOnline\Entities\Company * @covers \AtolOnline\Entities\Company::setSno - * @covers \AtolOnline\Exceptions\InvalidSnoException + * @covers \AtolOnline\Exceptions\InvalidEnumValueException */ public function testInvalidSnoException() { - $this->expectException(InvalidSnoException::class); + $this->expectException(InvalidEnumValueException::class); new Company('company@example.com', 'test', '1234567890', 'https://example.com'); } diff --git a/tests/ItemTest_todo.php b/tests/ItemTest_todo.php index 2e3b492..97d3f3f 100644 --- a/tests/ItemTest_todo.php +++ b/tests/ItemTest_todo.php @@ -14,11 +14,11 @@ use AtolOnline\{ Enums\PaymentMethods, Enums\PaymentObjects, Enums\VatTypes, - Exceptions\BasicTooManyException, Exceptions\TooHighPriceException, - Exceptions\TooLongNameException, + Exceptions\TooLongItemNameException, Exceptions\TooLongUnitException, - Exceptions\TooLongUserdataException,}; + Exceptions\TooLongUserdataException, + Exceptions\TooManyException,}; /** * Class ItemTest @@ -102,12 +102,12 @@ class ItemTestTodo extends BasicTestCase /** * Тестирует исключение о слишком длинном наименовании * - * @throws TooLongNameException + * @throws TooLongItemNameException */ public function testAtolNameTooLongException() { $item = new Item(); - $this->expectException(TooLongNameException::class); + $this->expectException(TooLongItemNameException::class); $item->setName(Helpers::randomStr(130)); } @@ -115,13 +115,13 @@ class ItemTestTodo extends BasicTestCase * Тестирует исключение о слишком высоком количестве * * @throws TooHighPriceException - * @throws BasicTooManyException + * @throws TooManyException * @throws TooLongUnitException */ public function testAtolQuantityTooHighException() { $item = new Item(); - $this->expectException(BasicTooManyException::class); + $this->expectException(TooManyException::class); $item->setQuantity(100000.1); } @@ -160,4 +160,4 @@ class ItemTestTodo extends BasicTestCase $this->expectException(TooLongUnitException::class); $item->setMeasurementUnit('кг кг кг кг кг кг кг кг кг '); } -} \ No newline at end of file +} diff --git a/tests/KktMonitorTest.php b/tests/KktMonitorTest.php index 88d9ad5..c8d53fd 100644 --- a/tests/KktMonitorTest.php +++ b/tests/KktMonitorTest.php @@ -56,7 +56,7 @@ class KktMonitorTest extends BasicTestCase $client = new KktMonitor(); $this->assertIsObject($client); $this->assertIsSameClass(KktMonitor::class, $client); - $this->assertExtendsClasses([AtolClient::class], $client); + $this->assertExtendsClasses($client, [AtolClient::class]); } /** @@ -76,8 +76,8 @@ class KktMonitorTest extends BasicTestCase { $client = new KktMonitor(false, 'login', 'password', []); $this->assertIsObject($client); - $this->assertIsSameClass(KktMonitor::class, $client); - $this->assertExtendsClasses([AtolClient::class], $client); + $this->assertIsSameClass($client, KktMonitor::class); + $this->assertExtendsClasses($client, [AtolClient::class]); } /** From 00b2643f4228f0bab858e8d2f8c0f83e065b5c95 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 22 Nov 2021 14:52:05 +0800 Subject: [PATCH 17/85] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B2=20Helpers=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Helpers.php | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/Helpers.php b/src/Helpers.php index 78240b8..ad0a348 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -52,4 +52,67 @@ final class Helpers } return $result; } + + /** + * Проверяет идентичность двух классов + * + * @param object|string $class1 + * @param object|string $class2 + * @return bool + */ + public static function isSameClass(object|string $class1, object|string $class2): bool + { + return (is_object($class1) ? $class1::class : $class1) === (is_object($class2) ? $class2::class : $class2); + } + + /** + * Тестирует наследование класса (объекта) от указанных классов + * + * @param object|string $class Объект или имя класса для проверки + * @param string[] $parents Имена классов-родителей + * @see https://www.php.net/manual/ru/function.class-parents.php + */ + public static function checkExtendsClasses(object|string $class, array $parents): bool + { + return self::checkClassesIntersection($parents, $class, 'class_parents'); + } + + /** + * Тестирует имплементацию классом (объектом) указанных интерфейсов + * + * @param object|string $actual Объект или имя класса для проверки + * @param string[] $interfaces Имена классов-интерфейсов + * @see https://www.php.net/manual/ru/function.class-implements.php + */ + public static function checkImplementsInterfaces(object|string $actual, array $interfaces): bool + { + return self::checkClassesIntersection($interfaces, $actual, 'class_implements'); + } + + /** + * Тестирует использование классом (объектом) указанных трейтов + * + * @param object|string $class + * @param string[] $traits + * @return bool + * @see https://www.php.net/manual/ru/function.class-uses.php + */ + public static function checkUsesTraits(array $traits, object|string $class): bool + { + return self::checkClassesIntersection($traits, $class, 'class_uses'); + } + + /** + * Проверяет пересечение классов указанной функцией SPL + * + * @param object|string $class Класс для проверки на вхождение, или объект, класс коего нужно проверить + * @param string[] $classes Массив классов, вхождение в который нужно проверить + * @param string $function class_parents|class_implements|class_uses + */ + protected static function checkClassesIntersection(array $classes, object|string $class, string $function): bool + { + $actual_classes = is_object($class) ? $function($class) : [$class::class]; + return is_array($actual_classes) + && !empty(array_intersect($classes, $actual_classes)); + } } \ No newline at end of file From 9d3344a0dfaeb823c8c442bd0125b1be6a312276 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 22 Nov 2021 14:52:27 +0800 Subject: [PATCH 18/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D1=8B=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=81?= =?UTF-8?q?=D1=82=D1=80=D1=83=D0=BA=D1=82=D0=BE=D1=80=D0=B5=20KktMonitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/KktMonitor.php | 4 ++-- src/Enum.php | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 src/Enum.php diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index 037af92..1e63643 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -56,8 +56,8 @@ class KktMonitor extends AtolClient protected function fetchAll(?int $limit = null, ?int $offset = null): KktResponse { $params = []; - $limit && $params['limit'] = $limit; - $offset && $params['offset'] = $offset; + !is_null($limit) && $params['limit'] = $limit; + !is_null($offset) && $params['offset'] = $offset; return $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params); } diff --git a/src/Enum.php b/src/Enum.php new file mode 100644 index 0000000..cfa866a --- /dev/null +++ b/src/Enum.php @@ -0,0 +1,23 @@ + Date: Mon, 22 Nov 2021 14:53:10 +0800 Subject: [PATCH 19/85] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=D0=BC=20AgentTypes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Enums/AgentTypes.php | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/Enums/AgentTypes.php diff --git a/src/Enums/AgentTypes.php b/src/Enums/AgentTypes.php new file mode 100644 index 0000000..923d5c4 --- /dev/null +++ b/src/Enums/AgentTypes.php @@ -0,0 +1,67 @@ + Date: Wed, 24 Nov 2021 01:30:54 +0800 Subject: [PATCH 20/85] =?UTF-8?q?=D0=A2=D1=83=D1=87=D0=B0=20=D0=B4=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - класс `PayingAgent`, покрытый тестами - новые константы для тегов ФФД 1.05 `Ffd105Tags` - `Entity::jsonSerialize()` object -> array (again) - `TooManyException::$max` int -> float - тесты по psr-4, потому что почему бы и нет - некоторые провайдеры вынесены в `BasicTestCase` - улучшен тест покупателя --- composer.json | 6 +- docs/monitoring.md | 2 +- src/Constants/Constraints.php | 30 +++- src/Constants/Ffd105Tags.php | 86 +++++++++++ src/Entities/Client.php | 19 +-- src/Entities/Company.php | 6 +- src/Entities/CorrectionInfo.php | 12 +- src/Entities/Kkt.php | 4 +- src/Entities/PayingAgent.php | 123 +++++++++++++++ src/Exceptions/EmptyEmailException.php | 8 +- src/Exceptions/InvalidEmailException.php | 8 +- src/Exceptions/InvalidInnLengthException.php | 9 +- .../InvalidPaymentAddressException.php | 5 +- src/Exceptions/InvalidPhoneException.php | 35 +++++ src/Exceptions/TooHighPriceException.php | 19 +-- .../TooLongCallbackUrlException.php | 2 +- src/Exceptions/TooLongCashierException.php | 5 +- .../TooLongClientContactException.php | 5 +- src/Exceptions/TooLongClientNameException.php | 5 +- src/Exceptions/TooLongEmailException.php | 8 +- src/Exceptions/TooLongException.php | 4 +- src/Exceptions/TooLongItemNameException.php | 9 +- src/Exceptions/TooLongLoginException.php | 2 +- src/Exceptions/TooLongPasswordException.php | 2 +- .../TooLongPayingAgentOperationException.php | 9 +- .../TooLongPaymentAddressException.php | 5 +- src/Exceptions/TooLongUnitException.php | 5 +- src/Exceptions/TooLongUserdataException.php | 5 +- src/Exceptions/TooManyException.php | 4 +- src/Exceptions/TooManyItemsException.php | 2 +- src/Exceptions/TooManyPaymentsException.php | 2 +- src/Exceptions/TooManyVatsException.php | 2 +- src/Helpers.php | 4 +- .../Tests/Api}/KktMonitorTest.php | 3 +- .../{ => AtolOnline/Tests}/BasicTestCase.php | 62 +++++++- .../Tests/Entities}/ClientTest.php | 137 +++++------------ .../Tests/Entities}/CompanyTest.php | 8 +- .../Tests/Entities}/KktEntityTest.php | 3 +- .../Tests/Entities/PayingAgentTest.php | 145 ++++++++++++++++++ tests/{ => AtolOnline/Tests}/HelpersTest.php | 4 +- 40 files changed, 622 insertions(+), 192 deletions(-) create mode 100644 src/Constants/Ffd105Tags.php create mode 100644 src/Entities/PayingAgent.php create mode 100644 src/Exceptions/InvalidPhoneException.php rename tests/{ => AtolOnline/Tests/Api}/KktMonitorTest.php (99%) rename tests/{ => AtolOnline/Tests}/BasicTestCase.php (77%) rename tests/{ => AtolOnline/Tests/Entities}/ClientTest.php (67%) rename tests/{ => AtolOnline/Tests/Entities}/CompanyTest.php (98%) rename tests/{ => AtolOnline/Tests/Entities}/KktEntityTest.php (98%) create mode 100644 tests/AtolOnline/Tests/Entities/PayingAgentTest.php rename tests/{ => AtolOnline/Tests}/HelpersTest.php (99%) diff --git a/composer.json b/composer.json index be3ec00..12a1f8e 100644 --- a/composer.json +++ b/composer.json @@ -57,9 +57,9 @@ } }, "autoload-dev": { - "psr-4": { - "AtolOnlineTests\\": "tests/" - } + "classmap": [ + "tests/" + ] }, "scripts": { "test": "vendor/bin/phpunit --colors=always", diff --git a/docs/monitoring.md b/docs/monitoring.md index 6fafe5f..c3dcf2f 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -113,4 +113,4 @@ $kkt = $monitor->getOne($kkts->first()->serialNumber); --- -[Вернуться к содержанию](readme.md) \ No newline at end of file +[Вернуться к содержанию](readme.md) diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index 44ad74a..2c17544 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -25,17 +25,17 @@ final class Constraints * Максимальная длина email */ const MAX_LENGTH_EMAIL = 64; - + /** * Максимальная длина логина ККТ */ const MAX_LENGTH_LOGIN = 100; - + /** * Максимальная длина пароля ККТ */ const MAX_LENGTH_PASSWORD = 100; - + /** * Максимальная длина адреса места расчётов */ @@ -116,17 +116,31 @@ final class Constraints /** * Максимальная длина имени кассира (1021) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32 */ const MAX_LENGTH_CASHIER_NAME = 64; - + /** * Регулярное выражание для валидации строки ИНН + * + * @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/client/inn" */ - const PATTERN_INN = "/(^[0-9]{10}$)|(^[0-9]{12}$)/"; - + 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 = "/^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/"; -} \ No newline at end of file + const PATTERN_CALLBACK_URL = /* @lang PhpRegExp */ + '/^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]' . + '([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/'; +} diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php new file mode 100644 index 0000000..f2b4003 --- /dev/null +++ b/src/Constants/Ffd105Tags.php @@ -0,0 +1,86 @@ +getName() && $json['name'] = $this->getName(); $this->getEmail() && $json['email'] = $this->getEmail(); $this->getPhone() && $json['phone'] = $this->getPhone(); $this->getInn() && $json['inn'] = $this->getInn(); - return (object)$json; + return $json; } } diff --git a/src/Entities/Company.php b/src/Entities/Company.php index df3f8f2..c0259a4 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -205,9 +205,9 @@ class Company extends Entity * @throws InvalidInnLengthException * @throws InvalidPaymentAddressException */ - public function jsonSerialize(): object + public function jsonSerialize(): array { - return (object)[ + return [ 'email' => $this->email ? $this->getEmail() : throw new InvalidEmailException(), @@ -222,4 +222,4 @@ class Company extends Entity : throw new InvalidPaymentAddressException(), ]; } -} \ No newline at end of file +} diff --git a/src/Entities/CorrectionInfo.php b/src/Entities/CorrectionInfo.php index 07f4122..6a06f82 100644 --- a/src/Entities/CorrectionInfo.php +++ b/src/Entities/CorrectionInfo.php @@ -123,12 +123,12 @@ class CorrectionInfo extends Entity /** * @inheritDoc */ - public function jsonSerialize(): object + public function jsonSerialize(): array { - return (object)[ - 'type' => $this->getType() ?? '', // обязателен - 'base_date' => $this->getDate() ?? '', // обязателен - 'base_number' => $this->getNumber() ?? '', // обязателен + return [ + 'type' => $this->getType() ?? '', + 'base_date' => $this->getDate() ?? '', + 'base_number' => $this->getNumber() ?? '', ]; } -} \ No newline at end of file +} diff --git a/src/Entities/Kkt.php b/src/Entities/Kkt.php index 3178188..fab9335 100644 --- a/src/Entities/Kkt.php +++ b/src/Entities/Kkt.php @@ -129,8 +129,8 @@ final class Kkt extends Entity /** * @inheritDoc */ - public function jsonSerialize() + public function jsonSerialize(): array { - return $this->data; + return (array)$this->data; } } diff --git a/src/Entities/PayingAgent.php b/src/Entities/PayingAgent.php new file mode 100644 index 0000000..606cfb3 --- /dev/null +++ b/src/Entities/PayingAgent.php @@ -0,0 +1,123 @@ +setOperation($operation); + $this->setPhones($phones); + } + + /** + * Устанавливает операцию + * + * @param string|null $operation + * @return $this + * @throws TooLongPayingAgentOperationException + */ + public function setOperation(?string $operation): self + { + if (!is_null($operation)) { + $operation = trim($operation); + if (mb_strlen($operation) > Constraints::MAX_LENGTH_PAYING_AGENT_OPERATION) { + throw new TooLongPayingAgentOperationException($operation); + } + } + $this->operation = empty($operation) ? null : $operation; + return $this; + } + + /** + * Вoзвращает установленную операцию + * + * @return string|null + */ + public function getOperation(): ?string + { + return $this->operation; + } + + /** + * Устанавливает массив номеров телефонов + * + * @param array|Collection|null $phones + * @return $this + * @throws InvalidPhoneException + */ + public function setPhones(array|Collection|null $phones): self + { + if (!is_null($phones)) { + $phones = is_array($phones) ? collect($phones) : $phones; + $phones->each(function ($phone) { + $phone = preg_replace('/[^\d]/', '', trim($phone)); + if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { + throw new InvalidPhoneException($phone); + } + }); + } + $this->phones = empty($phones) ? collect() : $phones; + return $this; + } + + /** + * Возвращает установленные + * + * @return Collection + */ + public function getPhones(): Collection + { + return $this->phones; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): array + { + $json = []; + $this->getOperation() && $json['operation'] = $this->getOperation(); + !$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray(); + return $json; + } +} diff --git a/src/Exceptions/EmptyEmailException.php b/src/Exceptions/EmptyEmailException.php index bb4200b..1993027 100644 --- a/src/Exceptions/EmptyEmailException.php +++ b/src/Exceptions/EmptyEmailException.php @@ -11,12 +11,18 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Ffd105Tags; + /** * Исключение, возникающее при попытке указать пустой email + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ class EmptyEmailException extends AtolException { protected $message = 'Email не может быть пустым'; - protected array $ffd_tags = [1008, 1117]; + protected array $ffd_tags = [ + Ffd105Tags::CLIENT_CONTACT, + Ffd105Tags::COMPANY_EMAIL, + ]; } diff --git a/src/Exceptions/InvalidEmailException.php b/src/Exceptions/InvalidEmailException.php index 148df7e..9b07ca9 100644 --- a/src/Exceptions/InvalidEmailException.php +++ b/src/Exceptions/InvalidEmailException.php @@ -11,13 +11,19 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Ffd105Tags; + /** * Исключение, возникающее при ошибке валидации email + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ class InvalidEmailException extends AtolException { - protected array $ffd_tags = [1008, 1117]; + protected array $ffd_tags = [ + Ffd105Tags::CLIENT_CONTACT, + Ffd105Tags::COMPANY_EMAIL, + ]; /** * Конструктор diff --git a/src/Exceptions/InvalidInnLengthException.php b/src/Exceptions/InvalidInnLengthException.php index a385add..e304125 100644 --- a/src/Exceptions/InvalidInnLengthException.php +++ b/src/Exceptions/InvalidInnLengthException.php @@ -11,12 +11,19 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Ffd105Tags; + /** * Исключение, возникающее при попытке указать ИНН некорректной длины */ class InvalidInnLengthException extends AtolException { - protected array $ffd_tags = [1016, 1018, 1226, 1228]; + protected array $ffd_tags = [ + Ffd105Tags::MTO_INN, + Ffd105Tags::COMPANY_INN, + Ffd105Tags::SUPPLIER_INN, + Ffd105Tags::CLIENT_INN, + ]; /** * Конструктор diff --git a/src/Exceptions/InvalidPaymentAddressException.php b/src/Exceptions/InvalidPaymentAddressException.php index e390da3..59a0bef 100644 --- a/src/Exceptions/InvalidPaymentAddressException.php +++ b/src/Exceptions/InvalidPaymentAddressException.php @@ -11,13 +11,16 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use AtolOnline\Constants\Ffd105Tags; + /** * Исключение, возникающее при попытке указать некорректный адрес места расчётов + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35 */ class InvalidPaymentAddressException extends AtolException { - protected array $ffd_tags = [1187]; + protected array $ffd_tags = [Ffd105Tags::COMPANY_PADDRESS]; /** * Конструктор diff --git a/src/Exceptions/InvalidPhoneException.php b/src/Exceptions/InvalidPhoneException.php new file mode 100644 index 0000000..3e0ac61 --- /dev/null +++ b/src/Exceptions/InvalidPhoneException.php @@ -0,0 +1,35 @@ +assertIsObject($entity); - $this->assertIsObject($entity->jsonSerialize()); + $this->assertIsArray($entity->jsonSerialize()); $this->assertIsString((string)$entity); $this->assertJson((string)$entity); if ($json_structure) { @@ -138,6 +137,23 @@ class BasicTestCase extends TestCase $this->assertIsSameClass($expected, Collection::class); } + //------------------------------------------------------------------------------------------------------------------ + + /** + * Провайдер строк, которые приводятся к null + * + * @return array> + */ + public function providerNullableStrings(): array + { + return [ + [''], + [' '], + [null], + ["\n\r\t"], + ]; + } + /** * Провайдер валидных телефонов * @@ -155,6 +171,22 @@ class BasicTestCase extends TestCase ]; } + /** + * Провайдер телефонов, которые приводятся к null + * + * @return array> + */ + public function providerNullablePhones(): array + { + return array_merge( + $this->providerNullableStrings(), + [ + [Helpers::randomStr(10, false)], + ["asdfgvs \n\rtt\t*/(*&%^*$%"], + ] + ); + } + /** * Провайдер валидных email-ов * @@ -170,4 +202,24 @@ class BasicTestCase extends TestCase ['abc.def@mail-archive.com'], ]; } -} \ No newline at end of file + + /** + * Провайдер невалидных email-ов + * + * @return array> + */ + public function providerInvalidEmails(): array + { + return [ + ['@example'], + [Helpers::randomStr(15)], + ['@example.com'], + ['abc.def@mail'], + ['.abc@mail.com'], + ['example@example'], + ['abc..def@mail.com'], + ['abc.def@mail..com'], + ['abc.def@mail#archive.com'], + ]; + } +} diff --git a/tests/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php similarity index 67% rename from tests/ClientTest.php rename to tests/AtolOnline/Tests/Entities/ClientTest.php index 0442646..52f05c9 100644 --- a/tests/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnlineTests; +namespace AtolOnline\Tests\Entities; use AtolOnline\{ Entities\Client, @@ -16,91 +16,28 @@ use AtolOnline\{ Exceptions\TooLongClientContactException, Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException, - Exceptions\TooLongItemNameException, - Helpers}; + Helpers, + Tests\BasicTestCase +}; /** - * Набор тестов для проверки работы класс покупателя + * Набор тестов для проверки работы класса покупателя */ class ClientTest extends BasicTestCase { /** - * Провайдер строк, которые приводятся к null - * - * @return array> - */ - public function providerNullableStrings(): array - { - return [ - [''], - [' '], - [null], - ["\n\r\t"], - ]; - } - - /** - * Провайдер телефонов, которые приводятся к null - * - * @return array> - */ - public function providerNullablePhones(): array - { - return array_merge( - $this->providerNullableStrings(), - [ - [Helpers::randomStr(10, false)], - ["asdfgvs \n\rtt\t*/(*&%^*$%"], - ] - ); - } - - /** - * Провайдер невалидных email-ов - * - * @return array> - */ - public function providerInvalidEmails(): array - { - return [ - ['@example'], - [Helpers::randomStr(15)], - ['@example.com'], - ['abc.def@mail'], - ['.abc@mail.com'], - ['example@example'], - ['abc..def@mail.com'], - ['abc.def@mail..com'], - ['abc.def@mail#archive.com'], - ]; - } - - //------------------------------------------------------------------------------------------------------------------ - - /** - * Тестирует приведение покупателя к json - * - * @covers \AtolOnline\Entities\Client - * @covers \AtolOnline\Entities\Client::jsonSerialize - */ - public function testAtolable(): void - { - $this->assertAtolable(new Client()); - } - - /** - * Тестирует конструктор покупателя без передачи значений + * Тестирует конструктор без передачи значений и приведение к json * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::jsonSerialize */ public function testConstructorWithoutArgs(): void { - $this->assertEquals('{}', (string)(new Client())); + $this->assertEquals('[]', (string)(new Client())); } /** - * Тестирует конструктор с передачей значений (внутри работают сеттеры) + * Тестирует конструктор с передачей значений и приведение к json * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::jsonSerialize @@ -108,16 +45,23 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Entities\Client::setEmail * @covers \AtolOnline\Entities\Client::setInn + * @covers \AtolOnline\Entities\Client::getName + * @covers \AtolOnline\Entities\Client::getPhone + * @covers \AtolOnline\Entities\Client::getEmail + * @covers \AtolOnline\Entities\Client::getInn */ public function testConstructorWithArgs(): void { - $customer = new Client( + $this->assertAtolable(new Client('John Doe'), ['name' => 'John Doe']); + $this->assertAtolable(new Client(email: 'john@example.com'), ['email' => 'john@example.com']); + $this->assertAtolable(new Client(phone: '+1/22/99*73s dsdas654 5s6'), ['phone' => '+122997365456']); + $this->assertAtolable(new Client(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); + $this->assertAtolable(new Client( 'John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', // +122997365456 '+fasd3\qe3fs_=nac99013928czc' // 3399013928 - ); - $this->assertAtolable($customer, [ + ), [ 'name' => 'John Doe', 'email' => 'john@example.com', 'phone' => '+122997365456', @@ -125,10 +69,8 @@ class ClientTest extends BasicTestCase ]); } - //------------------------------------------------------------------------------------------------------------------ - /** - * Тестирует установку имён покупателя, которые приводятся к null + * Тестирует установку имён, которые приводятся к null * * @param mixed $name * @dataProvider providerNullableStrings @@ -139,27 +81,25 @@ class ClientTest extends BasicTestCase */ public function testNullableNames(mixed $name): void { - $customer = (new Client())->setName($name); - $this->assertNull($customer->getName()); + $this->assertNull((new Client())->setName($name)->getName()); } /** - * Тестирует установку валидного имени покупателя + * Тестирует установку валидного имени * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setName * @covers \AtolOnline\Entities\Client::getName - * @throws TooLongItemNameException + * @throws TooLongClientNameException */ public function testValidName(): void { $name = Helpers::randomStr(); - $customer = (new Client())->setName($name); - $this->assertEquals($name, $customer->getName()); + $this->assertEquals($name, (new Client())->setName($name)->getName()); } /** - * Тестирует установку невалидного имени покупателя + * Тестирует установку невалидного имени * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setName @@ -174,7 +114,7 @@ class ClientTest extends BasicTestCase //------------------------------------------------------------------------------------------------------------------ /** - * Тестирует установку телефонов покупателя, которые приводятся к null + * Тестирует установку телефонов, которые приводятся к null * * @param mixed $phone * @dataProvider providerNullablePhones @@ -185,13 +125,13 @@ class ClientTest extends BasicTestCase */ public function testNullablePhones(mixed $phone): void { - $customer = (new Client())->setPhone($phone); - $this->assertNull($customer->getPhone()); + $this->assertNull((new Client())->setPhone($phone)->getPhone()); } /** - * Тестирует установку валидного телефона покупателя + * Тестирует установку валидного телефона * + * @todo актуализировать при доработатанной валидации * @dataProvider providerValidPhones * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone @@ -200,13 +140,13 @@ class ClientTest extends BasicTestCase */ public function testValidPhone(string $input, string $output): void { - $customer = (new Client())->setPhone($input); - $this->assertEquals($output, $customer->getPhone()); + $this->assertEquals($output, (new Client())->setPhone($input)->getPhone()); } /** - * Тестирует установку невалидного телефона покупателя + * Тестирует установку невалидного телефона * + * @todo актуализировать при доработатанной валидации * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Exceptions\TooLongClientContactException @@ -221,7 +161,7 @@ class ClientTest extends BasicTestCase //------------------------------------------------------------------------------------------------------------------ /** - * Тестирует установку валидных email-ов покупателя + * Тестирует установку валидных email-ов * * @param mixed $email * @dataProvider providerValidEmails @@ -233,12 +173,11 @@ class ClientTest extends BasicTestCase */ public function testValidEmails(mixed $email): void { - $customer = (new Client())->setEmail($email); - $this->assertEquals($email, $customer->getEmail()); + $this->assertEquals($email, (new Client())->setEmail($email)->getEmail()); } /** - * Тестирует установку слишком длинного email покупателя + * Тестирует установку слишком длинного email * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setEmail @@ -253,7 +192,7 @@ class ClientTest extends BasicTestCase } /** - * Тестирует установку невалидного email покупателя + * Тестирует установку невалидного email * * @param mixed $email * @dataProvider providerInvalidEmails @@ -281,10 +220,8 @@ class ClientTest extends BasicTestCase */ public function testValidInn(): void { - $customer = (new Client())->setInn('1234567890'); - $this->assertEquals('1234567890', $customer->getInn()); - $customer = $customer->setInn('123456789012'); - $this->assertEquals('123456789012', $customer->getInn()); + $this->assertEquals('1234567890', (new Client())->setInn('1234567890')->getInn()); + $this->assertEquals('123456789012', (new Client())->setInn('123456789012')->getInn()); } /** diff --git a/tests/CompanyTest.php b/tests/AtolOnline/Tests/Entities/CompanyTest.php similarity index 98% rename from tests/CompanyTest.php rename to tests/AtolOnline/Tests/Entities/CompanyTest.php index 93fa09c..b392678 100644 --- a/tests/CompanyTest.php +++ b/tests/AtolOnline/Tests/Entities/CompanyTest.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnlineTests; +namespace AtolOnline\Tests\Entities; use AtolOnline\{ Entities\Company, @@ -18,7 +18,9 @@ use AtolOnline\{ Exceptions\InvalidPaymentAddressException, Exceptions\TooLongEmailException, Exceptions\TooLongPaymentAddressException, - Helpers}; + Helpers, + Tests\BasicTestCase +}; /** * Набор тестов для проверки работы класс продавца @@ -131,4 +133,4 @@ class CompanyTest extends BasicTestCase $this->expectException(InvalidPaymentAddressException::class); new Company('company@example.com', SnoTypes::OSN, '1234567890', ''); } -} \ No newline at end of file +} diff --git a/tests/KktEntityTest.php b/tests/AtolOnline/Tests/Entities/KktEntityTest.php similarity index 98% rename from tests/KktEntityTest.php rename to tests/AtolOnline/Tests/Entities/KktEntityTest.php index 6f2cfd7..6e6cbba 100644 --- a/tests/KktEntityTest.php +++ b/tests/AtolOnline/Tests/Entities/KktEntityTest.php @@ -9,11 +9,12 @@ declare(strict_types = 1); -namespace AtolOnlineTests; +namespace AtolOnline\Tests\Entities; use AtolOnline\Entities\Kkt; use AtolOnline\Exceptions\EmptyMonitorDataException; use AtolOnline\Exceptions\NotEnoughMonitorDataException; +use AtolOnline\Tests\BasicTestCase; use DateTime; use Exception; diff --git a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php new file mode 100644 index 0000000..afadb13 --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php @@ -0,0 +1,145 @@ +assertEquals('[]', (string)(new PayingAgent())); + } + + /** + * Тестирует конструктор с передачей значений и корректное приведение к json + * + * @covers \AtolOnline\Entities\PayingAgent + * @covers \AtolOnline\Entities\PayingAgent::jsonSerialize + * @covers \AtolOnline\Entities\PayingAgent::setOperation + * @covers \AtolOnline\Entities\PayingAgent::setPhones + * @covers \AtolOnline\Entities\PayingAgent::getOperation + * @covers \AtolOnline\Entities\PayingAgent::getPhones + * @throws InvalidPhoneException + * @throws TooLongPayingAgentOperationException + */ + public function testConstructorWithArgs(): void + { + $operation = Helpers::randomStr(); + $this->assertAtolable(new PayingAgent( + $operation, + ['+122997365456'], + ), [ + 'operation' => $operation, + 'phones' => ['+122997365456'], + ]); + $this->assertAtolable( + new PayingAgent($operation), + ['operation' => $operation] + ); + $this->assertAtolable( + new PayingAgent(phones: ['+122997365456']), + ['phones' => ['+122997365456']] + ); + } + + /** + * Тестирует установку операций, которые приводятся к null + * + * @param mixed $name + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\PayingAgent + * @covers \AtolOnline\Entities\PayingAgent::setOperation + * @covers \AtolOnline\Entities\PayingAgent::getOperation + * @throws TooLongPayingAgentOperationException + */ + public function testNullableOperations(mixed $name): void + { + $this->assertNull((new PayingAgent())->setOperation($name)->getOperation()); + } + + /** + * Тестирует установку невалидного имени покупателя + * + * @covers \AtolOnline\Entities\PayingAgent + * @covers \AtolOnline\Entities\PayingAgent::setOperation + * @covers \AtolOnline\Exceptions\TooLongPayingAgentOperationException + */ + public function testTooLongPayingAgentOperationException(): void + { + $this->expectException(TooLongPayingAgentOperationException::class); + (new PayingAgent())->setOperation(Helpers::randomStr(25)); + } + + /** + * Провайдер массивов телефонов, которые приводятся к null + * + * @return array> + */ + public function providerNullablePhonesArrays(): array + { + return [ + [[]], + [null], + [collect()], + ]; + } + + /** + * Тестирует установку пустых телефонов + * + * @dataProvider providerNullablePhonesArrays + * @covers \AtolOnline\Entities\PayingAgent + * @covers \AtolOnline\Entities\PayingAgent::setPhones + * @covers \AtolOnline\Entities\PayingAgent::getPhones + * @throws InvalidPhoneException + * @throws TooLongPayingAgentOperationException + */ + public function testNullablePhones(mixed $phones): void + { + $agent = new PayingAgent(phones: $phones); + $this->assertIsCollection($agent->getPhones()); + $this->assertTrue($agent->getPhones()->isEmpty()); + } + + /** + * Тестирует установку невалидных телефонов + * + * @covers \AtolOnline\Entities\PayingAgent + * @covers \AtolOnline\Entities\PayingAgent::setPhones + * @covers \AtolOnline\Exceptions\InvalidPhoneException + * @throws InvalidPhoneException + */ + public function testInvalidPhoneException(): void + { + $this->expectException(InvalidPhoneException::class); + (new PayingAgent())->setPhones([ + '12345678901234567', // good + '+123456789012345678', // good + '12345678901234567890', // bad + '+12345678901234567890', // bad + ]); + } +} diff --git a/tests/HelpersTest.php b/tests/AtolOnline/Tests/HelpersTest.php similarity index 99% rename from tests/HelpersTest.php rename to tests/AtolOnline/Tests/HelpersTest.php index 7715516..70f0140 100644 --- a/tests/HelpersTest.php +++ b/tests/AtolOnline/Tests/HelpersTest.php @@ -7,7 +7,7 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnlineTests; +namespace AtolOnline\Tests; use AtolOnline\Helpers; @@ -120,4 +120,4 @@ class HelpersTest extends BasicTestCase $this->assertEquals($output, strlen($result)); // тестировать на наличие цифр быссмысленно } -} \ No newline at end of file +} From c5b57ec26decea1e910e7d181041a2c8b864c487 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 24 Nov 2021 17:54:04 +0800 Subject: [PATCH 21/85] =?UTF-8?q?=D0=9A=D0=BB=D0=B0=D1=81=D1=81=20`MoneyTr?= =?UTF-8?q?ansferOperator`,=20=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Также мелкофиксы по phpdoc `PayingAgent` и его тестам --- src/Constants/Ffd105Tags.php | 7 +- src/Entities/Kkt.php | 2 +- src/Entities/MoneyTransferOperator.php | 85 +++++++++++++++++ src/Entities/PayingAgent.php | 10 +- src/Enums/AgentTypes.php | 8 +- src/Exceptions/InvalidPhoneException.php | 3 +- .../Entities/MoneyTransferOperatorTest.php | 95 +++++++++++++++++++ .../Tests/Entities/PayingAgentTest.php | 7 +- 8 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 src/Entities/MoneyTransferOperator.php create mode 100644 tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index f2b4003..c7b71de 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -44,13 +44,18 @@ final class Ffd105Tags */ const COMPANY_PADDRESS = 1187; + /** + * Телефон оператора по приёму платежей + */ + const RPO_PHONES = 1074; + /** * ИНН оператора перевода */ const MTO_INN = 1016; /** - * Телефон платежного агента + * Телефон платёжного агента */ const PAGENT_PHONE = 1073; diff --git a/src/Entities/Kkt.php b/src/Entities/Kkt.php index fab9335..7203951 100644 --- a/src/Entities/Kkt.php +++ b/src/Entities/Kkt.php @@ -52,7 +52,7 @@ final class Kkt extends Entity 3 => 'Разрыв соединения при передаче документа на сервер', 4 => 'Некорректный заголовок сессионного пакета', 5 => 'Превышен таймаут ожидания квитанции', - 6 => 'Разрыв соединения при приеме квитанции', + 6 => 'Разрыв соединения при приёме квитанции', 7 => 'Превышен таймаут передачи документа на сервер', 8 => 'ОФД-процесс не иницилизирован', ]; diff --git a/src/Entities/MoneyTransferOperator.php b/src/Entities/MoneyTransferOperator.php new file mode 100644 index 0000000..ddddf66 --- /dev/null +++ b/src/Entities/MoneyTransferOperator.php @@ -0,0 +1,85 @@ +setPhones($phones); + } + + /** + * Возвращает установленные номера телефонов + * + * @todo вытащить в трейт + * @return Collection + */ + public function getPhones(): Collection + { + return $this->phones; + } + + /** + * Устанавливает массив номеров телефонов + * + * @todo вытащить в трейт + * @param array|Collection|null $phones + * @return $this + * @throws InvalidPhoneException + */ + public function setPhones(array|Collection|null $phones): self + { + if (!is_null($phones)) { + $phones = is_array($phones) ? collect($phones) : $phones; + $phones->each(function ($phone) { + $phone = preg_replace('/[^\d]/', '', trim($phone)); + if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { + throw new InvalidPhoneException($phone); + } + }); + } + $this->phones = empty($phones) ? collect() : $phones; + return $this; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): array + { + $json = []; + !$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray(); + return $json; + } +} diff --git a/src/Entities/PayingAgent.php b/src/Entities/PayingAgent.php index 606cfb3..69636c7 100644 --- a/src/Entities/PayingAgent.php +++ b/src/Entities/PayingAgent.php @@ -17,7 +17,7 @@ use AtolOnline\Exceptions\TooLongPayingAgentOperationException; use Illuminate\Support\Collection; /** - * Класс, описывающий данные платёжного агента + * Класс, описывающий платёжного агента * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19 */ @@ -29,7 +29,7 @@ class PayingAgent extends Entity protected ?string $operation = null; /** - * @var Collection Телефоны платежного агента (1073) + * @var Collection Телефоны платёжного агента (1073) */ protected Collection $phones; @@ -37,7 +37,7 @@ class PayingAgent extends Entity * Конструктор * * @param string|null $operation Наименование операции (1044) - * @param array|Collection|null $phones Телефоны платежного агента (1073) + * @param array|Collection|null $phones Телефоны платёжного агента (1073) * @throws TooLongPayingAgentOperationException * @throws InvalidPhoneException */ @@ -81,6 +81,7 @@ class PayingAgent extends Entity /** * Устанавливает массив номеров телефонов * + * @todo вытащить в трейт * @param array|Collection|null $phones * @return $this * @throws InvalidPhoneException @@ -101,8 +102,9 @@ class PayingAgent extends Entity } /** - * Возвращает установленные + * Возвращает установленные номера телефонов * + * @todo вытащить в трейт * @return Collection */ public function getPhones(): Collection diff --git a/src/Enums/AgentTypes.php b/src/Enums/AgentTypes.php index 923d5c4..2fa51fc 100644 --- a/src/Enums/AgentTypes.php +++ b/src/Enums/AgentTypes.php @@ -23,22 +23,22 @@ use AtolOnline\Enum; 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'; diff --git a/src/Exceptions/InvalidPhoneException.php b/src/Exceptions/InvalidPhoneException.php index 3e0ac61..a2ad97f 100644 --- a/src/Exceptions/InvalidPhoneException.php +++ b/src/Exceptions/InvalidPhoneException.php @@ -14,13 +14,14 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Ffd105Tags; /** - * Исключение, возникающее при ошибке валидации телефона + * Исключение, возникающее при ошибке валидации номера телефона */ class InvalidPhoneException extends AtolException { protected array $ffd_tags = [ Ffd105Tags::CLIENT_CONTACT, Ffd105Tags::PAGENT_PHONE, + Ffd105Tags::RPO_PHONES, ]; /** diff --git a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php new file mode 100644 index 0000000..45b8800 --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php @@ -0,0 +1,95 @@ +assertEquals('[]', (string)(new MoneyTransferOperator())); + } + + /** + * Тестирует конструктор с передачей значений и корректное приведение к json + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::jsonSerialize + * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Entities\MoneyTransferOperator::getPhones + * @throws InvalidPhoneException + */ + public function testConstructorWithArgs(): void + { + $this->assertAtolable(new MoneyTransferOperator(['+122997365456']), ['phones' => ['+122997365456']]); + } + + /** + * Провайдер массивов телефонов, которые приводятся к null + * + * @return array + */ + public function providerNullablePhonesArrays(): array + { + return [ + [[]], + [null], + [collect()], + ]; + } + + /** + * Тестирует установку пустых телефонов + * + * @dataProvider providerNullablePhonesArrays + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Entities\MoneyTransferOperator::getPhones + * @throws InvalidPhoneException + */ + public function testNullablePhones(mixed $phones): void + { + $agent = new MoneyTransferOperator($phones); + $this->assertIsCollection($agent->getPhones()); + $this->assertTrue($agent->getPhones()->isEmpty()); + } + + /** + * Тестирует установку невалидных телефонов + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Exceptions\InvalidPhoneException + * @throws InvalidPhoneException + */ + public function testInvalidPhoneException(): void + { + $this->expectException(InvalidPhoneException::class); + (new MoneyTransferOperator())->setPhones([ + '12345678901234567', // good + '+123456789012345678', // good + '12345678901234567890', // bad + '+12345678901234567890', // bad + ]); + } +} diff --git a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php index afadb13..d835ae0 100644 --- a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php +++ b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php @@ -14,8 +14,7 @@ use AtolOnline\{ Exceptions\InvalidPhoneException, Exceptions\TooLongPayingAgentOperationException, Helpers, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; /** * Набор тестов для проверки работы класса платёжного агента @@ -81,7 +80,7 @@ class PayingAgentTest extends BasicTestCase } /** - * Тестирует установку невалидного имени покупателя + * Тестирует установку невалидной операции * * @covers \AtolOnline\Entities\PayingAgent * @covers \AtolOnline\Entities\PayingAgent::setOperation @@ -96,7 +95,7 @@ class PayingAgentTest extends BasicTestCase /** * Провайдер массивов телефонов, которые приводятся к null * - * @return array> + * @return array */ public function providerNullablePhonesArrays(): array { From 3b75c8b983e42e2693dc6b8759ec7c28222d3b0f Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 24 Nov 2021 17:57:24 +0800 Subject: [PATCH 22/85] =?UTF-8?q?=D0=9A=D0=BB=D0=B0=D1=81=D1=81=20`Supplie?= =?UTF-8?q?r`,=20=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D1=8B=D0=B9=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Это было слишком легко :( --- src/Entities/Supplier.php | 85 +++++++++++++++++ .../Tests/Entities/SupplierTest.php | 95 +++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 src/Entities/Supplier.php create mode 100644 tests/AtolOnline/Tests/Entities/SupplierTest.php diff --git a/src/Entities/Supplier.php b/src/Entities/Supplier.php new file mode 100644 index 0000000..4d58d08 --- /dev/null +++ b/src/Entities/Supplier.php @@ -0,0 +1,85 @@ +setPhones($phones); + } + + /** + * Возвращает установленные номера телефонов + * + * @todo вытащить в трейт + * @return Collection + */ + public function getPhones(): Collection + { + return $this->phones; + } + + /** + * Устанавливает массив номеров телефонов + * + * @todo вытащить в трейт + * @param array|Collection|null $phones + * @return $this + * @throws InvalidPhoneException + */ + public function setPhones(array|Collection|null $phones): self + { + if (!is_null($phones)) { + $phones = is_array($phones) ? collect($phones) : $phones; + $phones->each(function ($phone) { + $phone = preg_replace('/[^\d]/', '', trim($phone)); + if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { + throw new InvalidPhoneException($phone); + } + }); + } + $this->phones = empty($phones) ? collect() : $phones; + return $this; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): array + { + $json = []; + !$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray(); + return $json; + } +} diff --git a/tests/AtolOnline/Tests/Entities/SupplierTest.php b/tests/AtolOnline/Tests/Entities/SupplierTest.php new file mode 100644 index 0000000..d0df501 --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/SupplierTest.php @@ -0,0 +1,95 @@ +assertEquals('[]', (string)(new Supplier())); + } + + /** + * Тестирует конструктор с передачей значений и корректное приведение к json + * + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::jsonSerialize + * @covers \AtolOnline\Entities\Supplier::setPhones + * @covers \AtolOnline\Entities\Supplier::getPhones + * @throws InvalidPhoneException + */ + public function testConstructorWithArgs(): void + { + $this->assertAtolable(new Supplier(['+122997365456']), ['phones' => ['+122997365456']]); + } + + /** + * Провайдер массивов телефонов, которые приводятся к null + * + * @return array + */ + public function providerNullablePhonesArrays(): array + { + return [ + [[]], + [null], + [collect()], + ]; + } + + /** + * Тестирует установку пустых телефонов + * + * @dataProvider providerNullablePhonesArrays + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::setPhones + * @covers \AtolOnline\Entities\Supplier::getPhones + * @throws InvalidPhoneException + */ + public function testNullablePhones(mixed $phones): void + { + $agent = new Supplier($phones); + $this->assertIsCollection($agent->getPhones()); + $this->assertTrue($agent->getPhones()->isEmpty()); + } + + /** + * Тестирует установку невалидных телефонов + * + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::setPhones + * @covers \AtolOnline\Exceptions\InvalidPhoneException + * @throws InvalidPhoneException + */ + public function testInvalidPhoneException(): void + { + $this->expectException(InvalidPhoneException::class); + (new Supplier())->setPhones([ + '12345678901234567', // good + '+123456789012345678', // good + '12345678901234567890', // bad + '+12345678901234567890', // bad + ]); + } +} From 790831e9338405f539a6d04d2ffc4d7a97bd1730 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 24 Nov 2021 18:45:01 +0800 Subject: [PATCH 23/85] `MoneyTransferOperator` => `ReceivePaymentsOperator` --- ...erator.php => ReceivePaymentsOperator.php} | 6 ++-- ...st.php => ReceivePaymentsOperatorTest.php} | 36 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) rename src/Entities/{MoneyTransferOperator.php => ReceivePaymentsOperator.php} (89%) rename tests/AtolOnline/Tests/Entities/{MoneyTransferOperatorTest.php => ReceivePaymentsOperatorTest.php} (65%) diff --git a/src/Entities/MoneyTransferOperator.php b/src/Entities/ReceivePaymentsOperator.php similarity index 89% rename from src/Entities/MoneyTransferOperator.php rename to src/Entities/ReceivePaymentsOperator.php index ddddf66..06b0414 100644 --- a/src/Entities/MoneyTransferOperator.php +++ b/src/Entities/ReceivePaymentsOperator.php @@ -20,17 +20,17 @@ use Illuminate\Support\Collection; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19-20 */ -class MoneyTransferOperator extends Entity +class ReceivePaymentsOperator extends Entity { /** - * @var Collection Телефоны платёжного агента (1073) + * @var Collection Телефоны оператора по приёму платежей (1074) */ protected Collection $phones; /** * Конструктор * - * @param array|Collection|null $phones Телефон оператора по приёму платежей (1074) + * @param array|Collection|null $phones Телефоны оператора по приёму платежей (1074) * @throws InvalidPhoneException */ public function __construct( diff --git a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php b/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php similarity index 65% rename from tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php rename to tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php index 45b8800..ffe501e 100644 --- a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php @@ -10,38 +10,38 @@ namespace AtolOnline\Tests\Entities; use AtolOnline\{ - Entities\MoneyTransferOperator, + Entities\ReceivePaymentsOperator, Exceptions\InvalidPhoneException, Tests\BasicTestCase}; /** * Набор тестов для проверки работы класса оператора по приёму платежей */ -class MoneyTransferOperatorTest extends BasicTestCase +class ReceivePaymentsOperatorTest extends BasicTestCase { /** * Тестирует конструктор без передачи значений и корректное приведение к json * - * @covers \AtolOnline\Entities\MoneyTransferOperator - * @covers \AtolOnline\Entities\MoneyTransferOperator::jsonSerialize + * @covers \AtolOnline\Entities\ReceivePaymentsOperator + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::jsonSerialize */ public function testConstructorWithoutArgs(): void { - $this->assertEquals('[]', (string)(new MoneyTransferOperator())); + $this->assertEquals('[]', (string)(new ReceivePaymentsOperator())); } /** * Тестирует конструктор с передачей значений и корректное приведение к json * - * @covers \AtolOnline\Entities\MoneyTransferOperator - * @covers \AtolOnline\Entities\MoneyTransferOperator::jsonSerialize - * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones - * @covers \AtolOnline\Entities\MoneyTransferOperator::getPhones + * @covers \AtolOnline\Entities\ReceivePaymentsOperator + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::jsonSerialize + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::setPhones + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::getPhones * @throws InvalidPhoneException */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new MoneyTransferOperator(['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertAtolable(new ReceivePaymentsOperator(['+122997365456']), ['phones' => ['+122997365456']]); } /** @@ -62,14 +62,14 @@ class MoneyTransferOperatorTest extends BasicTestCase * Тестирует установку пустых телефонов * * @dataProvider providerNullablePhonesArrays - * @covers \AtolOnline\Entities\MoneyTransferOperator - * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones - * @covers \AtolOnline\Entities\MoneyTransferOperator::getPhones + * @covers \AtolOnline\Entities\ReceivePaymentsOperator + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::setPhones + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::getPhones * @throws InvalidPhoneException */ public function testNullablePhones(mixed $phones): void { - $agent = new MoneyTransferOperator($phones); + $agent = new ReceivePaymentsOperator($phones); $this->assertIsCollection($agent->getPhones()); $this->assertTrue($agent->getPhones()->isEmpty()); } @@ -77,19 +77,19 @@ class MoneyTransferOperatorTest extends BasicTestCase /** * Тестирует установку невалидных телефонов * - * @covers \AtolOnline\Entities\MoneyTransferOperator - * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Entities\ReceivePaymentsOperator + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::setPhones * @covers \AtolOnline\Exceptions\InvalidPhoneException * @throws InvalidPhoneException */ public function testInvalidPhoneException(): void { $this->expectException(InvalidPhoneException::class); - (new MoneyTransferOperator())->setPhones([ + (new ReceivePaymentsOperator([ '12345678901234567', // good '+123456789012345678', // good '12345678901234567890', // bad '+12345678901234567890', // bad - ]); + ])); } } From 95dbc3a5b74a82298e3b7e71a8af983e1eb59fb7 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 24 Nov 2021 18:54:48 +0800 Subject: [PATCH 24/85] =?UTF-8?q?=D0=9A=D0=BB=D0=B0=D1=81=D1=81=20`Supplie?= =?UTF-8?q?r`=20=D0=BE=D0=B1=D0=B7=D0=B0=D0=B2=D1=91=D0=BB=D1=81=D1=8F=20`?= =?UTF-8?q?name`=20+=20`inn`=20=D0=B8=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BA?= =?UTF-8?q?=D1=80=D1=8B=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Ffd105Tags.php | 17 +++- src/Entities/Supplier.php | 76 ++++++++++++++++- src/Exceptions/InvalidPhoneException.php | 2 + .../Tests/Entities/SupplierTest.php | 85 ++++++++++++++++++- 4 files changed, 173 insertions(+), 7 deletions(-) diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index c7b71de..1cd7c86 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -17,7 +17,7 @@ final class Ffd105Tags /** * Телефон или электронный адрес покупателя */ - const CLIENT_CONTACT = 1008; + const CLIENT_PHONE_EMAIL = 1008; /** * Наименование организации или фамилия, имя, отчество (при наличии), серия и номер паспорта покупателя (клиента) @@ -49,6 +49,11 @@ final class Ffd105Tags */ const RPO_PHONES = 1074; + /** + * Телефон оператора перевода + */ + const MTO_PHONES = 1075; + /** * ИНН оператора перевода */ @@ -59,6 +64,16 @@ final class Ffd105Tags */ const PAGENT_PHONE = 1073; + /** + * Телефон поставщика + */ + const SUPPLIER_PHONES = 1171; + + /** + * Наименование поставщика + */ + const SUPPLIER_NAME = 1225; + /** * ИНН поставщика */ diff --git a/src/Entities/Supplier.php b/src/Entities/Supplier.php index 4d58d08..3852885 100644 --- a/src/Entities/Supplier.php +++ b/src/Entities/Supplier.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace AtolOnline\Entities; use AtolOnline\Constants\Constraints; +use AtolOnline\Exceptions\InvalidInnLengthException; use AtolOnline\Exceptions\InvalidPhoneException; use Illuminate\Support\Collection; @@ -23,22 +24,62 @@ use Illuminate\Support\Collection; class Supplier extends Entity { /** - * @var Collection Телефоны платёжного агента (1073) + * @var string|null Наименование (1225) + */ + protected ?string $name = null; + + /** + * @var string|null ИНН (1226) + */ + protected ?string $inn = null; + + /** + * @var Collection Телефоны (1171) */ protected Collection $phones; /** * Конструктор * - * @param array|Collection|null $phones Телефон оператора по приёму платежей (1074) + * @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; + } + /** * Возвращает установленные номера телефонов * @@ -73,12 +114,43 @@ class Supplier extends Entity return $this; } + /** + * Возвращает установленный ИНН + * + * @return string|null + */ + public function getInn(): ?string + { + return $this->inn; + } + + /** + * Устанавливает ИНН + * + * @param string|null $inn + * @return $this + * @throws InvalidInnLengthException Некорректная длина ИНН + */ + public function setInn(?string $inn): self + { + if (is_string($inn)) { + $inn = preg_replace('/[^\d]/', '', trim($inn)); + if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { + throw new InvalidInnLengthException($inn); + } + } + $this->inn = empty($inn) ? null : $inn; + return $this; + } + /** * @inheritDoc */ public function jsonSerialize(): array { $json = []; + $this->getName() && $json['name'] = $this->getName(); + $this->getInn() && $json['inn'] = $this->getInn(); !$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray(); return $json; } diff --git a/src/Exceptions/InvalidPhoneException.php b/src/Exceptions/InvalidPhoneException.php index a2ad97f..27552d5 100644 --- a/src/Exceptions/InvalidPhoneException.php +++ b/src/Exceptions/InvalidPhoneException.php @@ -22,6 +22,8 @@ class InvalidPhoneException extends AtolException Ffd105Tags::CLIENT_CONTACT, Ffd105Tags::PAGENT_PHONE, Ffd105Tags::RPO_PHONES, + Ffd105Tags::MTO_PHONES, + Ffd105Tags::SUPPLIER_PHONES, ]; /** diff --git a/tests/AtolOnline/Tests/Entities/SupplierTest.php b/tests/AtolOnline/Tests/Entities/SupplierTest.php index d0df501..57fb253 100644 --- a/tests/AtolOnline/Tests/Entities/SupplierTest.php +++ b/tests/AtolOnline/Tests/Entities/SupplierTest.php @@ -11,6 +11,7 @@ namespace AtolOnline\Tests\Entities; use AtolOnline\{ Entities\Supplier, + Exceptions\InvalidInnLengthException, Exceptions\InvalidPhoneException, Tests\BasicTestCase}; @@ -35,13 +36,45 @@ class SupplierTest extends BasicTestCase * * @covers \AtolOnline\Entities\Supplier * @covers \AtolOnline\Entities\Supplier::jsonSerialize + * @covers \AtolOnline\Entities\Supplier::setName + * @covers \AtolOnline\Entities\Supplier::getName * @covers \AtolOnline\Entities\Supplier::setPhones * @covers \AtolOnline\Entities\Supplier::getPhones + * @covers \AtolOnline\Entities\Supplier::setInn + * @covers \AtolOnline\Entities\Supplier::getInn * @throws InvalidPhoneException + * @throws InvalidInnLengthException */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new Supplier(['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertAtolable(new Supplier('some name'), ['name' => 'some name']); + $this->assertAtolable(new Supplier(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); + $this->assertAtolable(new Supplier(phones: ['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertAtolable(new Supplier( + 'some name', + '+fasd3\qe3fs_=nac99013928czc', + ['+122997365456'], + ), [ + 'name' => 'some name', + 'inn' => '3399013928', + 'phones' => ['+122997365456'], + ]); + } + + /** + * Тестирует установку имён, которые приводятся к null + * + * @param mixed $name + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::setName + * @covers \AtolOnline\Entities\Supplier::getName + * @throws InvalidPhoneException + * @throws InvalidInnLengthException + */ + public function testNullableOperations(mixed $name): void + { + $this->assertNull((new Supplier($name))->getName()); } /** @@ -66,10 +99,11 @@ class SupplierTest extends BasicTestCase * @covers \AtolOnline\Entities\Supplier::setPhones * @covers \AtolOnline\Entities\Supplier::getPhones * @throws InvalidPhoneException + * @throws InvalidInnLengthException */ public function testNullablePhones(mixed $phones): void { - $agent = new Supplier($phones); + $agent = new Supplier(phones: $phones); $this->assertIsCollection($agent->getPhones()); $this->assertTrue($agent->getPhones()->isEmpty()); } @@ -81,15 +115,58 @@ class SupplierTest extends BasicTestCase * @covers \AtolOnline\Entities\Supplier::setPhones * @covers \AtolOnline\Exceptions\InvalidPhoneException * @throws InvalidPhoneException + * @throws InvalidInnLengthException */ public function testInvalidPhoneException(): void { $this->expectException(InvalidPhoneException::class); - (new Supplier())->setPhones([ + (new Supplier(phones: [ '12345678901234567', // good '+123456789012345678', // good '12345678901234567890', // bad '+12345678901234567890', // bad - ]); + ])); + } + + /** + * Тестирует исключение о корректной длине ИНН + * + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::setInn + * @covers \AtolOnline\Entities\Supplier::getInn + * @throws InvalidInnLengthException + */ + public function testValidInn(): void + { + $this->assertEquals('1234567890', (new Supplier())->setInn('1234567890')->getInn()); + $this->assertEquals('123456789012', (new Supplier())->setInn('123456789012')->getInn()); + } + + /** + * Тестирует исключение о некорректной длине ИНН (10 цифр) + * + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidInnLengthException + */ + public function testInvalidInn10(): void + { + $this->expectException(InvalidInnLengthException::class); + (new Supplier())->setInn('12345678901'); + } + + /** + * Тестирует исключение о некорректной длине ИНН (12 цифр) + * + * @covers \AtolOnline\Entities\Supplier + * @covers \AtolOnline\Entities\Supplier::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidInnLengthException + */ + public function testInvalidInn12(): void + { + $this->expectException(InvalidInnLengthException::class); + (new Supplier())->setInn('1234567890123'); } } From 8b79b2be51bf3486c1f15ac15b8998ddc4d7e362 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 24 Nov 2021 18:55:53 +0800 Subject: [PATCH 25/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Ffd105Tags::CLIENT_CONTACTS` => `CLIENT_PHONE_EMAIL` + мелочи по `Client`, `ClientTest` и `PayingAgentTest` --- src/Entities/Client.php | 24 ++++++------------- src/Exceptions/EmptyEmailException.php | 2 +- src/Exceptions/InvalidEmailException.php | 2 +- src/Exceptions/InvalidPhoneException.php | 2 +- .../TooLongClientContactException.php | 2 +- src/Exceptions/TooLongEmailException.php | 2 +- .../AtolOnline/Tests/Entities/ClientTest.php | 9 +------ .../Tests/Entities/PayingAgentTest.php | 7 +++--- 8 files changed, 17 insertions(+), 33 deletions(-) diff --git a/src/Entities/Client.php b/src/Entities/Client.php index ce5f41b..3d8376e 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -17,8 +17,7 @@ use AtolOnline\{ Exceptions\InvalidInnLengthException, Exceptions\TooLongClientContactException, Exceptions\TooLongClientNameException, - Exceptions\TooLongEmailException -}; + Exceptions\TooLongEmailException}; /** * Класс Client, описывающий сущность покупателя @@ -28,22 +27,22 @@ use AtolOnline\{ class Client extends Entity { /** - * @var string|null Наименование. Тег ФФД - 1227. + * @var string|null Наименование (1227) */ protected ?string $name = null; /** - * @var string|null Email. Тег ФФД - 1008. + * @var string|null Email (1008) */ protected ?string $email = null; /** - * @var string|null Телефон покупателя. Тег ФФД - 1008. + * @var string|null Телефон (1008) */ protected ?string $phone = null; /** - * @var string|null ИНН. Тег ФФД - 1228. + * @var string|null ИНН (1228) */ protected ?string $inn = null; @@ -52,7 +51,7 @@ class Client extends Entity * * @param string|null $name Наименование (1227) * @param string|null $phone Email (1008) - * @param string|null $email Телефон покупателя (1008) + * @param string|null $email Телефон (1008) * @param string|null $inn ИНН (1228) * @throws TooLongClientNameException * @throws TooLongClientContactException @@ -65,8 +64,7 @@ class Client extends Entity ?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); @@ -76,8 +74,6 @@ class Client extends Entity /** * Возвращает наименование покупателя * - * Тег ФФД - 1227 - * * @return string|null */ public function getName(): ?string @@ -88,8 +84,6 @@ class Client extends Entity /** * Устанавливает наименование покупателя * - * Тег ФФД - 1227 - * * @todo улучшить валидацию по Constraints::PATTERN_PHONE * @param string|null $name * @return $this @@ -142,8 +136,6 @@ class Client extends Entity /** * Возвращает установленный телефон * - * Тег ФФД - 1008 - * * @return string|null */ public function getPhone(): ?string @@ -154,8 +146,6 @@ class Client extends Entity /** * Устанавливает телефон * - * Тег ФФД - 1008 - * * @param string|null $phone Номер телефона * @return $this * @throws TooLongClientContactException diff --git a/src/Exceptions/EmptyEmailException.php b/src/Exceptions/EmptyEmailException.php index 1993027..ebe842e 100644 --- a/src/Exceptions/EmptyEmailException.php +++ b/src/Exceptions/EmptyEmailException.php @@ -22,7 +22,7 @@ class EmptyEmailException extends AtolException { protected $message = 'Email не может быть пустым'; protected array $ffd_tags = [ - Ffd105Tags::CLIENT_CONTACT, + Ffd105Tags::CLIENT_PHONE_EMAIL, Ffd105Tags::COMPANY_EMAIL, ]; } diff --git a/src/Exceptions/InvalidEmailException.php b/src/Exceptions/InvalidEmailException.php index 9b07ca9..dcc6617 100644 --- a/src/Exceptions/InvalidEmailException.php +++ b/src/Exceptions/InvalidEmailException.php @@ -21,7 +21,7 @@ use AtolOnline\Constants\Ffd105Tags; class InvalidEmailException extends AtolException { protected array $ffd_tags = [ - Ffd105Tags::CLIENT_CONTACT, + Ffd105Tags::CLIENT_PHONE_EMAIL, Ffd105Tags::COMPANY_EMAIL, ]; diff --git a/src/Exceptions/InvalidPhoneException.php b/src/Exceptions/InvalidPhoneException.php index 27552d5..6b5d597 100644 --- a/src/Exceptions/InvalidPhoneException.php +++ b/src/Exceptions/InvalidPhoneException.php @@ -19,7 +19,7 @@ use AtolOnline\Constants\Ffd105Tags; class InvalidPhoneException extends AtolException { protected array $ffd_tags = [ - Ffd105Tags::CLIENT_CONTACT, + Ffd105Tags::CLIENT_PHONE_EMAIL, Ffd105Tags::PAGENT_PHONE, Ffd105Tags::RPO_PHONES, Ffd105Tags::MTO_PHONES, diff --git a/src/Exceptions/TooLongClientContactException.php b/src/Exceptions/TooLongClientContactException.php index b98e0d0..c79bf1d 100644 --- a/src/Exceptions/TooLongClientContactException.php +++ b/src/Exceptions/TooLongClientContactException.php @@ -21,5 +21,5 @@ class TooLongClientContactException extends TooLongException { protected $message = 'Cлишком длинный телефон или email покупателя'; protected float $max = Constraints::MAX_LENGTH_CLIENT_CONTACT; - protected array $ffd_tags = [Ffd105Tags::CLIENT_CONTACT]; + protected array $ffd_tags = [Ffd105Tags::CLIENT_PHONE_EMAIL]; } diff --git a/src/Exceptions/TooLongEmailException.php b/src/Exceptions/TooLongEmailException.php index 4d0df4f..224bcb1 100644 --- a/src/Exceptions/TooLongEmailException.php +++ b/src/Exceptions/TooLongEmailException.php @@ -22,7 +22,7 @@ class TooLongEmailException extends TooLongException protected $message = 'Слишком длинный email'; protected float $max = Constraints::MAX_LENGTH_EMAIL; protected array $ffd_tags = [ - Ffd105Tags::CLIENT_CONTACT, + Ffd105Tags::CLIENT_PHONE_EMAIL, Ffd105Tags::COMPANY_EMAIL, ]; } diff --git a/tests/AtolOnline/Tests/Entities/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php index 52f05c9..1207c41 100644 --- a/tests/AtolOnline/Tests/Entities/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -17,8 +17,7 @@ use AtolOnline\{ Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException, Helpers, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; /** * Набор тестов для проверки работы класса покупателя @@ -111,8 +110,6 @@ class ClientTest extends BasicTestCase (new Client())->setName(Helpers::randomStr(400)); } - //------------------------------------------------------------------------------------------------------------------ - /** * Тестирует установку телефонов, которые приводятся к null * @@ -158,8 +155,6 @@ class ClientTest extends BasicTestCase (new Client())->setPhone('99999999999999999999999999999999999999999999999999999999999999999999999999'); } - //------------------------------------------------------------------------------------------------------------------ - /** * Тестирует установку валидных email-ов * @@ -208,8 +203,6 @@ class ClientTest extends BasicTestCase (new Client())->setEmail($email); } - //------------------------------------------------------------------------------------------------------------------ - /** * Тестирует исключение о корректной длине ИНН * diff --git a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php index d835ae0..0967ff0 100644 --- a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php +++ b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php @@ -67,16 +67,17 @@ class PayingAgentTest extends BasicTestCase /** * Тестирует установку операций, которые приводятся к null * - * @param mixed $name + * @param mixed $operation * @dataProvider providerNullableStrings * @covers \AtolOnline\Entities\PayingAgent * @covers \AtolOnline\Entities\PayingAgent::setOperation * @covers \AtolOnline\Entities\PayingAgent::getOperation * @throws TooLongPayingAgentOperationException + * @throws InvalidPhoneException */ - public function testNullableOperations(mixed $name): void + public function testNullableOperations(mixed $operation): void { - $this->assertNull((new PayingAgent())->setOperation($name)->getOperation()); + $this->assertNull((new PayingAgent($operation))->getOperation()); } /** From 16c8d8a6763dd334b1cee1412e304b36fbeb2b90 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 26 Nov 2021 09:16:46 +0800 Subject: [PATCH 26/85] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B8=20=D0=BF=D0=BE=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20`Mo?= =?UTF-8?q?neyTransferOperator`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Company.php | 18 +- src/Entities/MoneyTransferOperator.php | 189 ++++++++++++++++++ src/Entities/Supplier.php | 2 +- .../Entities/MoneyTransferOperatorTest.php | 177 ++++++++++++++++ 4 files changed, 376 insertions(+), 10 deletions(-) create mode 100644 src/Entities/MoneyTransferOperator.php create mode 100644 tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php diff --git a/src/Entities/Company.php b/src/Entities/Company.php index c0259a4..7061d8f 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -24,37 +24,37 @@ use AtolOnline\{ /** * Класс, описывающий сущность компании-продавца * - * @package AtolOnline\Entities + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ class Company extends Entity { /** - * @var string|null Почта. Тег ФФД - 1117. + * @var string|null Почта (1117) */ protected ?string $email; /** - * @var string|null Система налогообложения продавца. Тег ФФД - 1055. + * @var string|null Система налогообложения продавца (1055) */ protected ?string $sno; /** - * @var string|null ИНН. Тег ФФД - 1018. + * @var string|null ИНН (1018) */ protected ?string $inn; /** - * @var string|null Место расчётов (адрес интернет-магазина). Тег ФФД - 1187. + * @var string|null Место расчётов (адрес интернет-магазина) (1187) */ protected ?string $payment_address; /** * Company constructor. * - * @param string $sno - * @param string $inn - * @param string $payment_address - * @param string $email + * @param string $sno Система налогообложения продавца (1055) + * @param string $inn ИНН (1018) + * @param string $payment_address Место расчётов (адрес интернет-магазина) (1187) + * @param string $email Почта (1117) * @throws InvalidEmailException * @throws InvalidInnLengthException * @throws InvalidPaymentAddressException diff --git a/src/Entities/MoneyTransferOperator.php b/src/Entities/MoneyTransferOperator.php new file mode 100644 index 0000000..198bab8 --- /dev/null +++ b/src/Entities/MoneyTransferOperator.php @@ -0,0 +1,189 @@ +setName($name); + $this->setInn($inn); + $this->setAddress($address); + $this->setPhones($phones); + } + + /** + * Возвращает установленное наименование поставщика + * + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * Устанавливает наименование поставщика + * + * @param string|null $name + * @return $this + */ + public function setName(?string $name): self + { + // критерии валидной строки не описаны ни в схеме, ни в документации + $this->name = trim((string)$name) ?: null; + return $this; + } + + /** + * Возвращает установленный адрес места расчётов + * + * @return string|null + */ + public function getAddress(): ?string + { + return $this->address; + } + + /** + * Устанавливает адрес места расчётов + * + * @param string|null $address + * @return $this + */ + public function setAddress(?string $address): self + { + // критерии валидной строки не описаны ни в схеме, ни в документации + $this->address = trim((string)$address) ?: null; + return $this; + } + + /** + * Возвращает установленные номера телефонов + * + * @todo вытащить в трейт + * @return Collection + */ + public function getPhones(): Collection + { + return $this->phones; + } + + /** + * Устанавливает массив номеров телефонов + * + * @todo вытащить в трейт + * @param array|Collection|null $phones + * @return $this + * @throws InvalidPhoneException + */ + public function setPhones(array|Collection|null $phones): self + { + if (!is_null($phones)) { + $phones = is_array($phones) ? collect($phones) : $phones; + $phones->each(function ($phone) { + $phone = preg_replace('/[^\d]/', '', trim($phone)); + if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { + throw new InvalidPhoneException($phone); + } + }); + } + $this->phones = $phones ?? collect(); + return $this; + } + + /** + * Возвращает установленный ИНН + * + * @return string|null + */ + public function getInn(): ?string + { + return $this->inn; + } + + /** + * Устанавливает ИНН + * + * @param string|null $inn + * @return $this + * @throws InvalidInnLengthException Некорректная длина ИНН + */ + public function setInn(?string $inn): self + { + if (is_string($inn)) { + $inn = preg_replace('/[^\d]/', '', trim($inn)); + if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { + throw new InvalidInnLengthException($inn); + } + } + $this->inn = $inn ?: null; + return $this; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): array + { + $json = []; + $this->getName() && $json['name'] = $this->getName(); + $this->getInn() && $json['inn'] = $this->getInn(); + $this->getAddress() && $json['address'] = $this->getAddress(); + !$this->getPhones()->isEmpty() && $json['phones'] = $this->getPhones()->toArray(); + return $json; + } +} diff --git a/src/Entities/Supplier.php b/src/Entities/Supplier.php index 3852885..8ddcc40 100644 --- a/src/Entities/Supplier.php +++ b/src/Entities/Supplier.php @@ -19,7 +19,7 @@ use Illuminate\Support\Collection; /** * Класс, описывающий поставшика * - * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 20-21 + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29 */ class Supplier extends Entity { diff --git a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php new file mode 100644 index 0000000..0be356d --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php @@ -0,0 +1,177 @@ +assertEquals('[]', (string)(new MoneyTransferOperator())); + } + + /** + * Тестирует конструктор с передачей значений и корректное приведение к json + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::jsonSerialize + * @covers \AtolOnline\Entities\MoneyTransferOperator::setName + * @covers \AtolOnline\Entities\MoneyTransferOperator::getName + * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Entities\MoneyTransferOperator::getPhones + * @covers \AtolOnline\Entities\MoneyTransferOperator::setInn + * @covers \AtolOnline\Entities\MoneyTransferOperator::getInn + * @covers \AtolOnline\Entities\MoneyTransferOperator::setAddress + * @covers \AtolOnline\Entities\MoneyTransferOperator::getAddress + * @throws InvalidPhoneException + * @throws InvalidInnLengthException + */ + public function testConstructorWithArgs(): void + { + $this->assertAtolable(new MoneyTransferOperator('some name'), ['name' => 'some name']); + $this->assertAtolable(new MoneyTransferOperator(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); + $this->assertAtolable(new MoneyTransferOperator(address: 'London'), ['address' => 'London']); + $this->assertAtolable(new MoneyTransferOperator(phones: ['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertAtolable(new MoneyTransferOperator( + 'some name', + '+fasd3\qe3fs_=nac99013928czc', + 'London', + ['+122997365456'], + ), [ + 'name' => 'some name', + 'inn' => '3399013928', + 'address' => 'London', + 'phones' => ['+122997365456'], + ]); + } + + /** + * Тестирует установку имён, которые приводятся к null + * + * @param mixed $name + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setName + * @covers \AtolOnline\Entities\MoneyTransferOperator::getName + * @throws InvalidPhoneException + * @throws InvalidInnLengthException + */ + public function testNullableOperations(mixed $name): void + { + $this->assertNull((new MoneyTransferOperator($name))->getName()); + } + + /** + * Провайдер массивов телефонов, которые приводятся к null + * + * @return array + */ + public function providerNullablePhonesArrays(): array + { + return [ + [[]], + [null], + [collect()], + ]; + } + + /** + * Тестирует установку пустых телефонов + * + * @dataProvider providerNullablePhonesArrays + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Entities\MoneyTransferOperator::getPhones + * @throws InvalidPhoneException + * @throws InvalidInnLengthException + */ + public function testNullablePhones(mixed $phones): void + { + $agent = new MoneyTransferOperator(phones: $phones); + $this->assertIsCollection($agent->getPhones()); + $this->assertTrue($agent->getPhones()->isEmpty()); + } + + /** + * Тестирует установку невалидных телефонов + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setPhones + * @covers \AtolOnline\Exceptions\InvalidPhoneException + * @throws InvalidPhoneException + * @throws InvalidInnLengthException + */ + public function testInvalidPhoneException(): void + { + $this->expectException(InvalidPhoneException::class); + (new MoneyTransferOperator(phones: [ + '12345678901234567', // good + '+123456789012345678', // good + '12345678901234567890', // bad + '+12345678901234567890', // bad + ])); + } + + /** + * Тестирует исключение о корректной длине ИНН + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setInn + * @covers \AtolOnline\Entities\MoneyTransferOperator::getInn + * @throws InvalidInnLengthException + */ + public function testValidInn(): void + { + $this->assertEquals('1234567890', (new MoneyTransferOperator())->setInn('1234567890')->getInn()); + $this->assertEquals('123456789012', (new MoneyTransferOperator())->setInn('123456789012')->getInn()); + } + + /** + * Тестирует исключение о некорректной длине ИНН (10 цифр) + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidInnLengthException + */ + public function testInvalidInn10(): void + { + $this->expectException(InvalidInnLengthException::class); + (new MoneyTransferOperator())->setInn('12345678901'); + } + + /** + * Тестирует исключение о некорректной длине ИНН (12 цифр) + * + * @covers \AtolOnline\Entities\MoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::setInn + * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidInnLengthException + */ + public function testInvalidInn12(): void + { + $this->expectException(InvalidInnLengthException::class); + (new MoneyTransferOperator())->setInn('1234567890123'); + } +} From e0d792d3a46f7826b99ac917e8b57fd14e69ceb0 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 27 Nov 2021 17:59:50 +0800 Subject: [PATCH 27/85] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B8=20=D0=BF=D0=BE=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20`Ag?= =?UTF-8?q?entInfo`=20(=D0=BF=D0=B5=D1=80=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B8=D0=B7=20`Agent`)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/AgentInfo.php | 168 ++++++++++++++++++ tests/AtolOnline/Tests/BasicTestCase.php | 6 +- .../Tests/Entities/AgentInfoTest.php | 118 ++++++++++++ 3 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 src/Entities/AgentInfo.php create mode 100644 tests/AtolOnline/Tests/Entities/AgentInfoTest.php diff --git a/src/Entities/AgentInfo.php b/src/Entities/AgentInfo.php new file mode 100644 index 0000000..d311c3e --- /dev/null +++ b/src/Entities/AgentInfo.php @@ -0,0 +1,168 @@ +setType($type); + !is_null($paying_agent) && $this->setPayingAgent($paying_agent); + !is_null($receive_payments_operator) && $this->setReceivePaymentsOperator($receive_payments_operator); + !is_null($money_transfer_operator) && $this->setMoneyTransferOperator($money_transfer_operator); + } + + /** + * Возвращает установленный признак оператора + * + * @return string|null + */ + public function getType(): ?string + { + return $this->type; + } + + /** + * Устанавливает признак оператора + * + * @param string|null $type + * @return AgentInfo + */ + public function setType(?string $type): AgentInfo + { + $this->type = $type; + return $this; + } + + /** + * Взвращает установленного платёжного агента + * + * @return PayingAgent|null + */ + public function getPayingAgent(): ?PayingAgent + { + return $this->paying_agent; + } + + /** + * Устанавливает платёжного агента + * + * @param PayingAgent|null $paying_agent + * @return AgentInfo + */ + public function setPayingAgent(?PayingAgent $paying_agent): AgentInfo + { + $this->paying_agent = $paying_agent; + return $this; + } + + /** + * Возвращает установленного оператора по приёму платежей + * + * @return ReceivePaymentsOperator|null + */ + public function getReceivePaymentsOperator(): ?ReceivePaymentsOperator + { + return $this->receive_payments_operator; + } + + /** + * Устанавливает оператора по приёму платежей + * + * @param ReceivePaymentsOperator|null $receive_payments_operator + * @return AgentInfo + */ + public function setReceivePaymentsOperator(?ReceivePaymentsOperator $receive_payments_operator): AgentInfo + { + $this->receive_payments_operator = $receive_payments_operator; + return $this; + } + + /** + * Возвращает установленного оператора перевода + * + * @return MoneyTransferOperator|null + */ + public function getMoneyTransferOperator(): ?MoneyTransferOperator + { + return $this->money_transfer_operator; + } + + /** + * Устанавливает оператора перевода + * + * @param MoneyTransferOperator|null $money_transfer_operator + * @return AgentInfo + */ + public function setMoneyTransferOperator(?MoneyTransferOperator $money_transfer_operator): AgentInfo + { + $this->money_transfer_operator = $money_transfer_operator; + return $this; + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): array + { + $json = []; + $this->getType() && $json['type'] = $this->getType(); + $this->getPayingAgent()?->jsonSerialize() && $json['paying_agent'] = $this + ->getPayingAgent()->jsonSerialize(); + $this->getReceivePaymentsOperator()?->jsonSerialize() && $json['receive_payments_operator'] = $this + ->getReceivePaymentsOperator()->jsonSerialize(); + $this->getMoneyTransferOperator()?->jsonSerialize() && $json['money_transfer_operator'] = $this + ->getMoneyTransferOperator()->jsonSerialize(); + return $json; + } +} diff --git a/tests/AtolOnline/Tests/BasicTestCase.php b/tests/AtolOnline/Tests/BasicTestCase.php index a1e144d..3d1f26d 100644 --- a/tests/AtolOnline/Tests/BasicTestCase.php +++ b/tests/AtolOnline/Tests/BasicTestCase.php @@ -67,16 +67,16 @@ class BasicTestCase extends TestCase * Тестирует является ли объект приводимым к json-строке согласно схеме АТОЛ Онлайн * * @param Entity $entity - * @param array $json_structure + * @param array|null $json_structure * @covers \AtolOnline\Entities\Entity::jsonSerialize * @covers \AtolOnline\Entities\Entity::__toString */ - public function assertAtolable(Entity $entity, array $json_structure = []): void + public function assertAtolable(Entity $entity, ?array $json_structure = null): void { $this->assertIsArray($entity->jsonSerialize()); $this->assertIsString((string)$entity); $this->assertJson((string)$entity); - if ($json_structure) { + if (!is_null($json_structure)) { $this->assertEquals(json_encode($json_structure), (string)$entity); } } diff --git a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php new file mode 100644 index 0000000..4c9e2ac --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php @@ -0,0 +1,118 @@ +assertAtolable(new AgentInfo(), []); + } + + /** + * Тестирует конструктор с передачей значений и корректное приведение к json + * + * @covers \AtolOnline\Entities\AgentInfo + * @covers \AtolOnline\Entities\AgentInfo::jsonSerialize + * @covers \AtolOnline\Entities\AgentInfo::setType + * @covers \AtolOnline\Entities\AgentInfo::getType + * @covers \AtolOnline\Entities\AgentInfo::setPayingAgent + * @covers \AtolOnline\Entities\AgentInfo::getPayingAgent + * @covers \AtolOnline\Entities\PayingAgent::jsonSerialize + * @covers \AtolOnline\Entities\AgentInfo::setMoneyTransferOperator + * @covers \AtolOnline\Entities\AgentInfo::getMoneyTransferOperator + * @covers \AtolOnline\Entities\MoneyTransferOperator::jsonSerialize + * @covers \AtolOnline\Entities\AgentInfo::setReceivePaymentsOperator + * @covers \AtolOnline\Entities\AgentInfo::getReceivePaymentsOperator + * @covers \AtolOnline\Entities\ReceivePaymentsOperator::jsonSerialize + * @throws InvalidPhoneException + * @throws TooLongPayingAgentOperationException + * @throws InvalidInnLengthException + * @throws InvalidEnumValueException + */ + public function testConstructorWithArgs(): void + { + $this->assertAtolable(new AgentInfo(null), []); + $this->assertAtolable(new AgentInfo(AgentTypes::ANOTHER), ['type' => AgentTypes::ANOTHER]); + $this->assertAtolable(new AgentInfo(paying_agent: new PayingAgent()), []); + $this->assertAtolable(new AgentInfo(money_transfer_operator: new MoneyTransferOperator()), []); + $this->assertAtolable(new AgentInfo(receive_payments_operator: new ReceivePaymentsOperator()), []); + + $this->assertAtolable(new AgentInfo( + AgentTypes::ANOTHER, + new PayingAgent(), + new ReceivePaymentsOperator(), + new MoneyTransferOperator(), + ), ['type' => AgentTypes::ANOTHER]); + + $this->assertAtolable(new AgentInfo( + AgentTypes::ANOTHER, + new PayingAgent('test', ['+79518888888']), + new ReceivePaymentsOperator(['+79519999999']), + new MoneyTransferOperator('MTO Name', '9876543210', 'London', ['+79517777777']), + ), [ + 'type' => AgentTypes::ANOTHER, + 'paying_agent' => [ + 'operation' => 'test', + 'phones' => [ + '+79518888888', + ], + ], + 'receive_payments_operator' => [ + 'phones' => [ + '+79519999999', + ], + ], + 'money_transfer_operator' => [ + 'name' => 'MTO Name', + 'inn' => '9876543210', + 'address' => 'London', + 'phones' => [ + "+79517777777", + ], + ], + ]); + } + + /** + * Тестирует исключение при некорректном типе + * + * @covers \AtolOnline\Entities\AgentInfo + * @covers \AtolOnline\Enums\AgentTypes::isValid + * @covers \AtolOnline\Exceptions\InvalidEnumValueException + */ + public function testInvalidEnumValueException(): void + { + $this->expectException(InvalidEnumValueException::class); + new AgentInfo('qwerty'); + } +} From f5480328436dcf2d859a43549232c51bdfd5d710 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 27 Nov 2021 18:53:23 +0800 Subject: [PATCH 28/85] =?UTF-8?q?=D0=A1=D1=81=D1=8B=D0=BB=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=82=D0=B5=D0=BB=D0=B5=D0=B3=D1=80=D0=B0=D0=BC?= =?UTF-8?q?=20=D0=B8=20=D0=BF=D0=B0=D1=82=D1=80=D0=B5=D0=BE=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 6 +++--- composer.json | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index bd422ca..7df57ce 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -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' ] diff --git a/composer.json b/composer.json index 12a1f8e..e51c624 100644 --- a/composer.json +++ b/composer.json @@ -28,11 +28,17 @@ } ], "support": { + "rss": "https://t.me/atolonline_php", + "chat": "https://t.me/+Rky7ia68fctjZmUy", "source": "https://github.com/anthonyaxenov/atol-online", "issues": "https://github.com/anthonyaxenov/atol-online/issues", "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" From 9d2617858de7444815fecb16d0a4b50277870ce4 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 28 Nov 2021 00:44:43 +0800 Subject: [PATCH 29/85] =?UTF-8?q?`AgentInfo`:=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D1=82=D0=B8=D0=BF=D0=B0=20=D0=B0=D0=B3=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=20=D0=B8=D0=B7=20=D0=BA=D0=BE=D0=BD=D1=81?= =?UTF-8?q?=D1=82=D1=80=D1=83=D0=BA=D1=82=D0=BE=D1=80=D0=B0=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=B5=D1=82=D1=82=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/AgentInfo.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Entities/AgentInfo.php b/src/Entities/AgentInfo.php index d311c3e..346cd4a 100644 --- a/src/Entities/AgentInfo.php +++ b/src/Entities/AgentInfo.php @@ -56,7 +56,7 @@ class AgentInfo extends Entity ?ReceivePaymentsOperator $receive_payments_operator = null, ?MoneyTransferOperator $money_transfer_operator = null, ) { - !is_null($type) && AgentTypes::isValid($type) && $this->setType($type); + !is_null($type) && $this->setType($type); !is_null($paying_agent) && $this->setPayingAgent($paying_agent); !is_null($receive_payments_operator) && $this->setReceivePaymentsOperator($receive_payments_operator); !is_null($money_transfer_operator) && $this->setMoneyTransferOperator($money_transfer_operator); @@ -77,10 +77,11 @@ class AgentInfo extends Entity * * @param string|null $type * @return AgentInfo + * @throws InvalidEnumValueException */ public function setType(?string $type): AgentInfo { - $this->type = $type; + AgentTypes::isValid($type) && $this->type = $type; return $this; } From c9670a13217c20b98989173c3e7b081c18a18cdd Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 28 Nov 2021 00:49:53 +0800 Subject: [PATCH 30/85] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B1=D0=B5=D1=81=D0=BF=D0=BE=D0=BB=D0=B5=D0=B7=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20`ItemArray`,=20`VatArray`=20=D0=B8=20`PaymentArr?= =?UTF-8?q?ay`,=20=D0=B1=D1=83=D0=B4=D1=83=D1=82=20=D0=B7=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=D1=8B=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D1=8F=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/ItemArray.php | 115 --------------------------------- src/Entities/PaymentArray.php | 113 -------------------------------- src/Entities/VatArray.php | 117 ---------------------------------- 3 files changed, 345 deletions(-) delete mode 100644 src/Entities/ItemArray.php delete mode 100644 src/Entities/PaymentArray.php delete mode 100644 src/Entities/VatArray.php diff --git a/src/Entities/ItemArray.php b/src/Entities/ItemArray.php deleted file mode 100644 index 09ee0dc..0000000 --- a/src/Entities/ItemArray.php +++ /dev/null @@ -1,115 +0,0 @@ -set($items); - } - } - - /** - * Устанавливает массив предметов расчёта - * - * @param Item[] $items Массив предметов расчёта - * @return $this - * @throws TooManyItemsException Слишком много предметов расчёта - */ - public function set(array $items): ItemArray - { - if ($this->validateCount($items)) { - $this->items = $items; - } - return $this; - } - - /** - * Добавляет предмет расчёта в массив - * - * @param Item $item Объект предмета расчёта - * @return $this - * @throws TooManyItemsException Слишком много предметов расчёта - */ - public function add(Item $item): ItemArray - { - if ($this->validateCount()) { - $this->items[] = $item; - } - return $this; - } - - /** - * Возвращает массив предметов расчёта - * - * @return Item[] - */ - public function get(): array - { - 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 TooManyItemsException Слишком много предметов расчёта - */ - protected function validateCount(?array $items = null): bool - { - if ((!empty($items) && count($items) >= self::MAX_COUNT) || count($this->items) >= self::MAX_COUNT) { - throw new TooManyItemsException(count($items), self::MAX_COUNT); - } - return true; - } -} \ No newline at end of file diff --git a/src/Entities/PaymentArray.php b/src/Entities/PaymentArray.php deleted file mode 100644 index a92997a..0000000 --- a/src/Entities/PaymentArray.php +++ /dev/null @@ -1,113 +0,0 @@ -set($payments); - } - } - - /** - * Устанавливает массив оплат - * - * @param Payment[] $payments - * @return $this - * @throws TooManyPaymentsException Слишком много оплат - */ - public function set(array $payments): PaymentArray - { - if ($this->validateCount($payments)) { - $this->payments = $payments; - } - return $this; - } - - /** - * Добавляет новую оплату к заданным - * - * @param Payment $payment Объект оплаты - * @return $this - * @throws TooManyPaymentsException Слишком много оплат - */ - public function add(Payment $payment): PaymentArray - { - if ($this->validateCount()) { - $this->payments[] = $payment; - } - return $this; - } - - /** - * Возвращает массив оплат - * - * @return Payment[] - */ - public function get(): array - { - 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 TooManyPaymentsException Слишком много оплат - */ - protected function validateCount(?array $payments = null): bool - { - if ((!empty($payments) && count($payments) >= self::MAX_COUNT) || count($this->payments) >= self::MAX_COUNT) { - throw new TooManyPaymentsException(count($payments), self::MAX_COUNT); - } - return true; - } -} \ No newline at end of file diff --git a/src/Entities/VatArray.php b/src/Entities/VatArray.php deleted file mode 100644 index db92ca7..0000000 --- a/src/Entities/VatArray.php +++ /dev/null @@ -1,117 +0,0 @@ -set($vats); - } - } - - /** - * Устанавливает массив ставок НДС - * - * @param Vat[] $vats Массив ставок НДС - * @return $this - * @throws TooManyVatsException Слишком много ставок НДС - */ - public function set(array $vats): VatArray - { - if ($this->validateCount($vats)) { - $this->vats = $vats; - } - return $this; - } - - /** - * Добавляет новую ставку НДС в массив - * - * @param Vat $vat Объект ставки НДС - * @return $this - * @throws TooManyVatsException Слишком много ставок НДС - */ - public function add(Vat $vat): VatArray - { - 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(): array - { - 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 TooManyVatsException Слишком много ставок НДС - */ - protected function validateCount(?array $vats = null): bool - { - if ((!empty($vats) && count($vats) >= self::MAX_COUNT) || count($this->vats) >= self::MAX_COUNT) { - throw new TooManyVatsException(count($vats), self::MAX_COUNT); - } - return true; - } -} \ No newline at end of file From e2141551d56977191449e87b8b167a33760c5518 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 28 Nov 2021 00:55:28 +0800 Subject: [PATCH 31/85] =?UTF-8?q?`Enum`=20=D1=81=D1=82=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=B0=D0=B1=D1=81=D1=82=D1=80=D0=B0=D0=BA=D1=82=D0=BD=D1=8B?= =?UTF-8?q?=D0=BC=20=D0=B2=D0=BD=D1=83=D1=82=D1=80=D0=B8=20`Enums`,=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=81=D0=BB=D0=B5=D0=B4=D0=BD=D0=B8=D0=BA=D0=B0?= =?UTF-8?q?=D0=BC=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D1=8B=20`getFfdTa?= =?UTF-8?q?gs()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Enum.php | 23 ------------ src/Enums/AgentTypes.php | 8 ++--- src/Enums/CorrectionTypes.php | 14 +++++--- src/Enums/DocumentTypes.php | 10 ++++-- src/Enums/Enum.php | 37 ++++++++++++++++++++ src/Enums/PaymentMethods.php | 16 +++++---- src/Enums/PaymentObjects.php | 16 +++++---- src/Enums/PaymentTypes.php | 22 +++++++----- src/Enums/SnoTypes.php | 8 ++--- src/Exceptions/InvalidEnumValueException.php | 2 +- src/Exceptions/TooLongUnitException.php | 2 +- 11 files changed, 95 insertions(+), 63 deletions(-) delete mode 100644 src/Enum.php create mode 100644 src/Enums/Enum.php diff --git a/src/Enum.php b/src/Enum.php deleted file mode 100644 index cfa866a..0000000 --- a/src/Enum.php +++ /dev/null @@ -1,23 +0,0 @@ - Date: Sun, 28 Nov 2021 00:55:34 +0800 Subject: [PATCH 32/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BE=D1=87=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B4=D0=B5=D0=BD=D0=B5=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Helpers.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Helpers.php b/src/Helpers.php index 5a02b8b..6275a5d 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -17,12 +17,12 @@ final class Helpers /** * Конвертирует копейки в рубли, оставляя только 2 знака после запятой * - * @param int|null $kopeks Копейки + * @param float|null $kopeks Копейки * @return float Рубли */ - public static function KopToRub(?int $kopeks): float + public static function toRub(?float $kopeks): float { - return round(abs((int)$kopeks) / 100, 2); + return round(abs((float)$kopeks) / 100, 2); } /** @@ -31,7 +31,7 @@ final class Helpers * @param float|null $rubles Рубли * @return int Копейки */ - public static function RubToKop(?float $rubles): int + public static function toKop(?float $rubles): int { return (int)round(abs((float)$rubles) * 100, 2); } From e0ff5a261a41d9b128487cdf9da5e37d5f310043 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 28 Nov 2021 00:57:07 +0800 Subject: [PATCH 33/85] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=D0=B8=20=D0=B2=20`Ffd105Tags`=20+=20=D0=BC=D0=B5?= =?UTF-8?q?=D0=BB=D0=BE=D1=87=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Ffd105Tags.php | 55 ++++++++++++++++++++++++++++++++---- src/Entities/Company.php | 14 +-------- src/Entities/Item.php | 18 ++++++------ src/TestEnvParams.php | 6 ++-- 4 files changed, 62 insertions(+), 31 deletions(-) diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index 1cd7c86..7b73a6d 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -40,7 +40,7 @@ final class Ffd105Tags const COMPANY_INN = 1008; /** - * Место расчетов + * Место расчётов */ const COMPANY_PADDRESS = 1187; @@ -85,22 +85,65 @@ final class Ffd105Tags const CASHIER = 1021; /** - * Наименование предмета расчета + * Наименование предмета расчёта */ const ITEM_NAME = 1030; /** - * Цена за единицу предмета расчета с учетом скидок и наценок + * Цена за единицу предмета расчёта с учётом скидок и наценок */ const ITEM_PRICE = 1079; /** - * Единица измерения предмета расчета + * Количество предмета расчёта */ - const ITEM_MEASURE = 1197; + const ITEM_QUANTITY = 1023; /** - * Дополнительный реквизит предмета расчета + * Стоимость предмета расчёта с учётом скидок и наценок + */ + const ITEM_SUM = 1043; + + /** + * Единица измерения предмета расчёта + */ + const ITEM_MEASUREMENT_UNIT = 1197; + + /** + * Код товара + */ + const ITEM_NOMENCLATURE_CODE = 1162; + + /** + * Признак способа расчёта + */ + const ITEM_PAYMENT_METHOD = 1214; + + /** + * Признак предмета расчёта + */ + const ITEM_PAYMENT_OBJECT = 1212; + + /** + * Дополнительный реквизит предмета расчёта */ const ITEM_USERDATA = 1191; + + /** + * Сумма акциза с учётом копеек, включённая в стоимость предмета расчёта + */ + const ITEM_PAYMENT_EXSICE = 1229; + + /** + * Цифровой код страны происхождения товара в соответствии с Общероссийским классификатором стран мира + * + * @see https://ru.wikipedia.org/wiki/Общероссийский_классификатор_стран_мира + * @see https://classifikators.ru/oksm + */ + const ITEM_COUNTRY_CODE = 1230; + + /** + * Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155) + */ + const DECLARATION_NUMBER = 1231; } diff --git a/src/Entities/Company.php b/src/Entities/Company.php index 7061d8f..73868d7 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -49,7 +49,7 @@ class Company extends Entity protected ?string $payment_address; /** - * Company constructor. + * Конструктор * * @param string $sno Система налогообложения продавца (1055) * @param string $inn ИНН (1018) @@ -107,8 +107,6 @@ class Company extends Entity /** * Возвращает установленный тип налогообложения * - * Тег ФФД - 1055 - * * @return string */ public function getSno(): string @@ -119,8 +117,6 @@ class Company extends Entity /** * Устанавливает тип налогообложения * - * Тег ФФД - 1055 - * * @param string $sno * @return $this * @throws InvalidEnumValueException @@ -136,8 +132,6 @@ class Company extends Entity /** * Возвращает установленный ИНН * - * Тег ФФД - 1018 - * * @return string */ public function getInn(): string @@ -148,8 +142,6 @@ class Company extends Entity /** * Устанавливает ИНН * - * Тег ФФД - 1018 - * * @param string $inn * @return $this * @throws InvalidInnLengthException @@ -167,8 +159,6 @@ class Company extends Entity /** * Возвращает установленный адрес места расчётов * - * Тег ФФД - 1187 - * * @return string */ public function getPaymentAddress(): string @@ -179,8 +169,6 @@ class Company extends Entity /** * Устанавливает адрес места расчётов * - * Тег ФФД - 1187 - * * @param string $payment_address * @return $this * @throws TooLongPaymentAddressException diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 1d0c95b..9421193 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -22,32 +22,32 @@ use AtolOnline\{ /** * Предмет расчёта (товар, услуга) * - * @package AtolOnline\Entities + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21-30 */ class Item extends Entity { /** - * @var string Наименование. Тег ФФД - 1030. + * @var string Наименование (1030) */ protected string $name; /** - * @var int Цена в копейках (с учётом скидок и наценок). Тег ФФД - 1079. + * @var int Цена в копейках (с учётом скидок и наценок) (1079) */ protected int $price = 0; /** - * @var float Количество, вес. Тег ФФД - 1023. + * @var float Количество, вес (1023) */ protected float $quantity = 0.0; /** - * @var float Сумма в копейках. Тег ФФД - 1043. + * @var float Сумма в копейках (1043) */ protected float $sum = 0; /** - * @var string Единица измерения количества. Тег ФФД - 1197. + * @var string Единица измерения количества (1197) */ protected string $measurement_unit; @@ -57,17 +57,17 @@ class Item extends Entity protected ?Vat $vat; /** - * @var string Признак способа расчёта. Тег ФФД - 1214. + * @var string Признак способа расчёта (1214) */ protected string $payment_method; /** - * @var string Признак объекта расчёта. Тег ФФД - 1212. + * @var string Признак объекта расчёта (1212) */ protected string $payment_object; /** - * @var string Дополнительный реквизит. Тег ФФД - 1191. + * @var string Дополнительный реквизит (1191) */ protected string $user_data; diff --git a/src/TestEnvParams.php b/src/TestEnvParams.php index f501189..bd78361 100644 --- a/src/TestEnvParams.php +++ b/src/TestEnvParams.php @@ -12,11 +12,11 @@ declare(strict_types = 1); namespace AtolOnline; /** - * Константы, определяющие параметры тестовой среды для ФФД 1.05 + * Константы, определяющие параметры тестовых сред * * @see https://online.atol.ru/files/ffd/test_sreda.txt Параметры настройки тестовых сред */ -class TestEnvParams +final class TestEnvParams { /** * Возвращает данные для работы с тестовой средой АТОЛ Онлайн ФФД 1.05 @@ -55,4 +55,4 @@ class TestEnvParams 'endpoint_ofd' => '', ]; } -} \ No newline at end of file +} From a7205ff75473fd55b26f58d4f54f9008931b0964 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 28 Nov 2021 00:58:05 +0800 Subject: [PATCH 34/85] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=20`Vat`,=20=D0=BF=D0=BE=D0=BA?= =?UTF-8?q?=D1=80=D1=8B=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Vat.php | 188 +++++++----------- .../AtolOnline/Tests/Entities/ClientTest.php | 2 +- tests/AtolOnline/Tests/Entities/VatTest.php | 101 ++++++++++ tests/AtolOnline/Tests/HelpersTest.php | 8 +- tests/VatTest_todo.php | 55 ----- 5 files changed, 174 insertions(+), 180 deletions(-) create mode 100644 tests/AtolOnline/Tests/Entities/VatTest.php delete mode 100644 tests/VatTest_todo.php diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index 9e85402..e133fea 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -12,78 +12,52 @@ declare(strict_types = 1); namespace AtolOnline\Entities; use AtolOnline\Enums\VatTypes; +use AtolOnline\Helpers; /** * Класс, описывающий ставку НДС * - * @package AtolOnline\Entities + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25, 31 */ class Vat extends Entity { /** - * @var string Выбранный тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106. + * @var string Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106) */ private string $type; - + /** - * @var int Сумма в копейках, от которой пересчитывается размер НДС + * @var float Сумма в рублях, от которой пересчитывается размер НДС */ - private int $sum_original = 0; - + private float $sum; + /** - * @var int Сумма НДС в копейках - */ - private int $sum_final = 0; - - /** - * Vat constructor. + * Конструктор * - * @param string $type Тип ставки НДС - * @param float|null $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС + * @param string $type Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106) + * @param float $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС */ - public function __construct(string $type = VatTypes::NONE, float $rubles = null) + public function __construct(string $type, float $rubles) { - $this->type = $type; - if ($rubles) { - $this->setSum($rubles); - } + $this->setType($type)->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 + * @param string $type Тип ставки НДС + * @return $this */ - protected static function calculator(string $type, int $kopeks) + public function setType(string $type): self { - 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; + $type = trim($type); + VatTypes::isValid($type) && $this->type = $type; + return $this; } - + /** - * Возвращает тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106. + * Возвращает тип ставки НДС * * @return string */ @@ -91,45 +65,7 @@ class Vat extends Entity { return $this->type; } - - /** - * Устанавливает тип ставки НДС. Тег ФФД - 1199, 1105, 1104, 1103, 1102, 1107, 1106. - * Автоматически пересчитывает итоговый размер НДС от исходной суммы. - * - * @param string $type Тип ставки НДС - * @return $this - */ - public function setType(string $type): Vat - { - $this->type = $type; - $this->setFinal(); - return $this; - } - - /** - * Возвращает расчитанный итоговый размер ставки НДС в рублях. Тег ФФД - 1200. - * - * @return float - */ - public function getFinalSum(): float - { - return rubles($this->sum_final); - } - - /** - * Устанавливает исходную сумму, от которой будет расчитываться итоговый размер НДС. - * Автоматически пересчитывает итоговый размер НДС от исходной суммы. - * - * @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС - * @return $this - */ - public function setSum(float $rubles): Vat - { - $this->sum_original = kopeks($rubles); - $this->setFinal(); - return $this; - } - + /** * Возвращает исходную сумму, от которой расчитывается размер налога * @@ -137,52 +73,64 @@ class Vat extends Entity */ public function getSum(): float { - return rubles($this->sum_original); + return $this->sum; } - + /** - * Прибавляет указанную сумму к общей исходной сумме. - * Автоматически пересчитывает итоговый размер НДС от новой исходной суммы. + * Устанавливает исходную сумму, от которой будет расчитываться итоговый размер НДС. + * Автоматически пересчитывает итоговый размер НДС от исходной суммы. + * + * @param float $rubles Сумма в рублях за предмет расчёта, из которой высчитывается размер НДС + * @return $this + */ + public function setSum(float $rubles): self + { + $this->sum = $rubles; + return $this; + } + + /** + * Возвращает sdрасчитанный итоговый размер ставки НДС в рублях + * + * @return float + * @see https://nalog-nalog.ru/nds/nalogovaya_baza_nds/kak-schitat-nds-pravilno-vychislyaem-20-ot-summy-primer-algoritm/ + * @see https://glavkniga.ru/situations/k500734 + * @see https://www.b-kontur.ru/nds-kalkuljator-online + */ + public function getCalculated(): float + { + $kopeks = Helpers::toKop($this->sum); + return Helpers::toRub(match ($this->getType()) { + VatTypes::VAT10 => $kopeks * 10 / 100, + VatTypes::VAT18 => $kopeks * 18 / 100, + VatTypes::VAT20 => $kopeks * 20 / 100, + VatTypes::VAT110 => $kopeks * 10 / 110, + VatTypes::VAT118 => $kopeks * 18 / 118, + VatTypes::VAT120 => $kopeks * 20 / 120, + default => 0, + }); + } + + /** + * Прибавляет указанную сумму к исходной * * @param float $rubles * @return $this */ - public function addSum(float $rubles): Vat + public function addSum(float $rubles): self { - $this->sum_original += kopeks($rubles); - $this->setFinal(); + $this->sum += $rubles; return $this; } - - /** - * Расчитывает и возвращает размер НДС от указанной суммы в рублях. - * Не изменяет итоговый размер НДС. - * - * @param float|null $rubles - * @return float - */ - public function calc(float $rubles): float - { - return rubles(self::calculator($this->type, kopeks($rubles))); - } - + /** * @inheritDoc */ - public function jsonSerialize() + public function jsonSerialize(): array { return [ 'type' => $this->getType(), - 'sum' => $this->getFinalSum(), + 'sum' => $this->getCalculated(), ]; } - - /** - * Расчитывает и устанавливает итоговый размер ставки от исходной суммы в копейках - */ - protected function setFinal(): Vat - { - $this->sum_final = self::calculator($this->type, $this->sum_original); - return $this; - } -} \ No newline at end of file +} diff --git a/tests/AtolOnline/Tests/Entities/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php index 1207c41..c2b4c34 100644 --- a/tests/AtolOnline/Tests/Entities/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -32,7 +32,7 @@ class ClientTest extends BasicTestCase */ public function testConstructorWithoutArgs(): void { - $this->assertEquals('[]', (string)(new Client())); + $this->assertAtolable(new Client(), []); } /** diff --git a/tests/AtolOnline/Tests/Entities/VatTest.php b/tests/AtolOnline/Tests/Entities/VatTest.php new file mode 100644 index 0000000..ba62404 --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/VatTest.php @@ -0,0 +1,101 @@ +assertAtolable($vat, [ + 'type' => $vat->getType(), + 'sum' => $vat->getCalculated(), + ]); + $this->assertEquals($type, $vat->getType()); + $this->assertEquals($sum, $vat->getSum()); + } + + /** + * Тестирует расчёт суммы НДС от исходной суммы 100+20р и 100-20р + * + * @dataProvider providerVatsAdd + * @param string $type Тип НДС + * @param float $after_plus Результат после +20р + * @param float $after_minus Результат после -20р + * @covers \AtolOnline\Entities\Vat::addSum + * @covers \AtolOnline\Entities\Vat::getCalculated + */ + public function testVatAdd(string $type, float $after_plus, float $after_minus) + { + $vat = (new Vat($type, 100))->addSum(20); // 120р + $this->assertEquals($after_plus, $vat->getCalculated()); + $vat->addSum(-20); // 100р + $this->assertEquals($after_minus, $vat->getCalculated()); + } +} diff --git a/tests/AtolOnline/Tests/HelpersTest.php b/tests/AtolOnline/Tests/HelpersTest.php index 70f0140..79e90c9 100644 --- a/tests/AtolOnline/Tests/HelpersTest.php +++ b/tests/AtolOnline/Tests/HelpersTest.php @@ -84,11 +84,11 @@ class HelpersTest extends BasicTestCase * Тестирует перевод копеек в рубли * * @dataProvider providerKopeksToRubles - * @covers \AtolOnline\Helpers::KopToRub + * @covers \AtolOnline\Helpers::toRub */ public function testKopeksToRubles(?int $kopeks, float $rubles): void { - $result = Helpers::KopToRub($kopeks); + $result = Helpers::toRub($kopeks); $this->assertIsFloat($result); $this->assertEquals($result, $rubles); } @@ -97,11 +97,11 @@ class HelpersTest extends BasicTestCase * Тестирует перевод копеек в рубли * * @dataProvider providerRublesToKopeks - * @covers \AtolOnline\Helpers::RubToKop + * @covers \AtolOnline\Helpers::toKop */ public function testRublesToKopeks(?float $rubles, int $kopeks): void { - $result = Helpers::RubToKop($rubles); + $result = Helpers::toKop($rubles); $this->assertIsInt($result); $this->assertEquals($result, $kopeks); } diff --git a/tests/VatTest_todo.php b/tests/VatTest_todo.php deleted file mode 100644 index 17eaa01..0000000 --- a/tests/VatTest_todo.php +++ /dev/null @@ -1,55 +0,0 @@ -assertEquals(0, $vat->getFinalSum(), 'Test ' . $vat_type . ' | 1 step'); - $vat->setSum($sum); - $this->assertEquals($expected_set, $vat->getFinalSum(), 'Test ' . $vat_type . ' | 2 step'); - $vat->addSum(20); - $this->assertEquals($expected_add, $vat->getFinalSum(), 'Test ' . $vat_type . ' | 3 step'); - $vat->addSum(-20); - } - - /** - * Провайдер данных для тестирования разных типов ставок НДС - * - * @return array - */ - public function vatProvider() - { - return [ - [VatTypes::NONE, 100, 0, 0], - [VatTypes::VAT0, 100, 0, 0], - [VatTypes::VAT10, 100, 9.09, 10.9], - [VatTypes::VAT18, 100, 15.25, 18.3], - ]; - } -} \ No newline at end of file From bce21f96586df7f7c2fb939857c5675e5b0c8936 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 28 Nov 2021 12:01:28 +0800 Subject: [PATCH 35/85] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Актуализирована часть про `Vat`, пересобрал README, мелочи --- README.md | 113 +++++++++++++++++-------------------------------- docs/kkt.md | 23 +++++++++- docs/readme.md | 35 ++++++++++++++- docs/vats.md | 106 +++++++++------------------------------------- 4 files changed, 115 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index 7da0f09..301019b 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,8 @@ --- -**В этой ветке проводится глубокий рефакторинг и активная подготовка к `v1.0.0`.** - -**Актуальность документации -- околонулевая.** - -**Общая работоспособность -- нулевая.** +**В этой ветке проводится глубокий рефакторинг, стабилизация и активная подготовка к `v1.0.0`. Документация +актуализируется постепенно.** --- @@ -15,29 +12,48 @@ Библиотека для фискализации чеков по 54-ФЗ через [облачную ККТ АТОЛ](https://online.atol.ru/). -Текущая поддерживаемая версия API: **5.1** - **[Документация](/docs/readme.md)** +Текущие поддерживаемые версии: + +| Протокол | API | ФФД | Статус | +|----------|-----|------|-------------| +| v4 | 5.7 | 1.05 | Рефакторинг | +| v5 | 2.0 | 1.2 | В планах | + +## Плюшечки + +* Мониторинг ККТ и ФН +* Фискализация докумнетов на облачной ККТ +* Валидация данных до отправки документа на ККТ (насколько это возможно, согласно схеме) +* Расчёты денег в копейках +* Фактически полное покрытие тестами +* PSR-4 автозагрузка + ## Системные требования -* 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` — абсолютный путь к корневой директории вашего проекта. + > При использовании фреймворков это обычно не требуется. ### Тестирование кода библиотеки @@ -46,80 +62,29 @@ Для запуска тестов необходимо перейти в корень вашего проекта и выполнить команду: ```bash -./vendor/bin/phpunit +composer test ``` -## Настройка ККТ - -Для работы с облачной ККТ необходимы следующие параметры: -* логин; -* пароль; -* код группы. - -Чтоб получить их, нужно: -1. авторизоваться на [online.atol.ru](https://online.atol.ru/lk/Account/Login); -2. на странице [Мои компании](https://online.atol.ru/lk/Company/List) нажать кнопку **Настройки интегратора**. - Скачается XML-файл с нужными настройками. - -Также для работы потребуются: -* ИНН продавца; -* URL места расчёта (ссылка на ваш интернет-сервис). - ## Использование библиотеки -### Доступные методы и классы - Весь исходный код находится в директории [`/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/) + **[Документация к библиотеке](/docs/readme.md)** +* Telegram-канал: [@atolonline_php](https://t.me/atolonline_php) +* Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md) ## Лицензия -Вы имеете право использовать код из этого репозитория только на условиях **[лицензии MIT](LICENSE)**. +Вы имеете право использовать и распространят код из этого репозитория на условиях **[лицензии MIT](LICENSE)**. diff --git a/docs/kkt.md b/docs/kkt.md index 060a364..aae3c72 100644 --- a/docs/kkt.md +++ b/docs/kkt.md @@ -4,6 +4,27 @@ --- +## Доступ к ККТ + +Для работы с облачной ККТ необходимы следующие параметры: + +* логин; +* пароль; +* код группы. + +Чтоы получить их, нужно: + +1. авторизоваться в личном кабинете [online.atol.ru](https://online.atol.ru/lk/Account/Login); +2. на странице [Мои компании](https://online.atol.ru/lk/Company/List) нажать кнопку **Настройки интегратора**. + Скачается XML-файл с нужными настройками. + +Также для работы потребуются: + +* ИНН продавца; +* URL места расчёта (ссылка на ваш интернет-сервис). + +## Использование + Объект ККТ инициализируется следующим образом: ```php @@ -293,4 +314,4 @@ $json_array = $item->jsonSerialize(); --- -[Вернуться к содержанию](readme.md) \ No newline at end of file +[Вернуться к содержанию](readme.md) diff --git a/docs/readme.md b/docs/readme.md index 3375be6..53dc201 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,6 +1,39 @@ # Документация к библиотеке -Содержание: +## Общий алгоритм + +1. Задать настройки ККТ +2. Собрать данные о покупателе +3. Собрать данные о продавце +4. Собрать данные о предметах расчёта (товары, услуги и пр.) +5. Создать документ, добавив в него покупателя, продавца и предметы расчёта +6. Отправить документ на регистрацию: + 6.1. *Необязательно:* задать `callback_url`, на который АТОЛ отправит HTTP POST о состоянии документа. +7. Запомнить `uuid` из пришедшего ответа, поскольку он пригодится для последующей проверки статуса фискализации. + > Если с документом был передан `callback_url`, то ответ придёт на этот самый URL. + Если с документом **НЕ** был передан `callback_url` **либо** callback от АТОЛа не пришёл в течение 300 секунд (5 минут), нужно запрашивать вручную по `uuid`, пришедшему от АТОЛа в ответ на регистрацию документа. +8. Проверить состояние документа: + 8.1. взять `uuid` ответа, полученного на запрос фискализации; + 8.2. отправить его в запросе состояния документа. + > Данные о документе можно получить только в течение 32 суток после успешной фискализации. + +В зависимости от специфики бизнеса, в документах можно/нужно передавать также и другую информацию. Подробности в +документациях и исходниках. + +### Об отправке электронного чека покупателю + +После успешной фискализации документа покупатель автоматически получит уведомление **от ОФД**, который используется в +связке с вашей ККТ: + +* **по email**, если в документе указан email клиента; +* **по смс**: + * если в документе указан номер телефона клиента; + * если на стороне ОФД необходима и подключена услуга СМС-информирования (уточняйте подробности о своего ОФД). + +> Если заданы email и телефон, то ОФД отдаёт приоритет email. + +## Подготовка документа + 1. [Работа с клиентами (покупателями)](client.md) 2. [Работа с компанией (продавцом)](company.md) 3. [Работа с оплатами](payments.md) diff --git a/docs/vats.md b/docs/vats.md index 456dca4..371a447 100644 --- a/docs/vats.md +++ b/docs/vats.md @@ -9,113 +9,47 @@ Объект ставки НДС инициализируется следующим образом: ```php -$vat = new AtolOnline\Entities\Vat(); -``` +use AtolOnline\Entities\Vat; +use AtolOnline\Enums\VatTypes; -У объекта ставки должны быть указаны все следующие атрибуты: -* тип ставки (теги ФФД: 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 // тип ставки +$vat = new Vat( + VatTypes::VAT10, // тип ставки + 123.45 // сумма в рублях, от которой считать ставку ); - -// 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); // общая сумма в рублях +$vat->setType(VatTypes::VAT20) + ->setSum(100.15); // 123.45 заменится на 100.15 ``` -Сумму можно установить и до установки типа ставки. -Объект её запомнит и пересчитает итоговый размер налога при смене типа ставки: +Общую сумму, из которой расчитывается размер налога, можно увеличить, используя метод `addSum()`. Указанная в рублях +сумма увеличится на указанные рубли. Для уменьшения суммы следует передать отрицательное число. ```php -$vat = (new AtolOnline\Entities\Vat()) - ->setSum(150) // общая сумма в рублях - ->setType(AtolOnline\Constants\VatTypes::VAT10); // тип ставки +$vat->addSum(40) // 100.15 + 40 = 140.15р + ->addSum(-15); // 140.15 - 15 = 125.15р ``` -Получить установленную расчётную сумму в рублях можно через геттер `getSum()`: +Получить установленную сумму можно через геттер `getSum()`: ```php -var_dump($vat->getSum()); -// double(150) +$vat->getSum(); // 125.15р ``` -Получить расчитанный размер налога в рублях можно через геттер `getFinalSum()`: +Размер налога по ставке высчитывается из этой общей суммы. Не смотря на то, что геттер и сеттер работают с рублями, ** +расчёты производятся в копейках**. Сделать это можно через `getCalculated()`: ```php -var_dump($vat->getFinalSum()); -// double(15): для примера выше это 10% от 150р = 15р +$vat->getCalculated(); +// для примера выше это 20% от 125.15р = 25.03р ``` -Общую сумму, из которой расчитывается размер налога, можно увеличить, используя метод `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`: +Объект класса приводится к JSON-строке автоматически или принудительно: ```php echo $vat; @@ -191,4 +125,4 @@ $json_array = $vat_array->jsonSerialize(); --- -[Вернуться к содержанию](readme.md) \ No newline at end of file +[Вернуться к содержанию](readme.md) From 5ccb0e9db4e12195bfc775b2c795a2f4b43630b7 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 1 Dec 2021 20:11:08 +0800 Subject: [PATCH 36/85] =?UTF-8?q?=D0=9A=D1=83=D1=87=D0=B0=D0=B4=20=D0=B4?= =?UTF-8?q?=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BE=D0=BA,=20=D0=B3?= =?UTF-8?q?=D0=BB=D0=B0=D0=B2=D0=BD=D1=8B=D0=BC=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=BE=D0=BC=20=D0=B2=D0=BE=D0=BA=D1=80=D1=83=D0=B3=20`It?= =?UTF-8?q?em`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Item` почти готов и весь покрыт тестами. Пока остались нереализованными `nomenclature_code` и `excise` - `Client::setPhone()` теперь выбрасывает InvalidPhoneException - доработка и создание новых исключений (не буду все перечислять, смотри диффы) - мелочи по phpdoc и всяким текстовкам --- src/Constants/Constraints.php | 24 +- src/Constants/Ffd105Tags.php | 2 +- src/Entities/Client.php | 12 +- src/Entities/Document.php | 4 +- src/Entities/Item.php | 435 +++++++----- src/Exceptions/AtolException.php | 3 +- src/Exceptions/EmptyItemNameException.php | 25 + .../InvalidDeclarationNumberException.php | 32 + src/Exceptions/InvalidEnumValueException.php | 13 +- src/Exceptions/InvalidOKSMCodeException.php | 34 + src/Exceptions/NegativeItemPriceException.php | 33 + .../NegativeItemQuantityException.php | 33 + .../TooHighItemQuantityException.php | 35 + src/Exceptions/TooHighPriceException.php | 12 +- src/Exceptions/TooHighSumException.php | 35 + ...hp => TooLongMeasurementUnitException.php} | 2 +- src/Exceptions/TooManyException.php | 13 +- .../AtolOnline/Tests/Entities/ClientTest.php | 16 +- .../AtolOnline/Tests/Entities/CompanyTest.php | 5 +- tests/AtolOnline/Tests/Entities/ItemTest.php | 659 ++++++++++++++++++ .../Entities/MoneyTransferOperatorTest.php | 2 +- tests/ItemTest_todo.php | 163 ----- 22 files changed, 1232 insertions(+), 360 deletions(-) create mode 100644 src/Exceptions/EmptyItemNameException.php create mode 100644 src/Exceptions/InvalidDeclarationNumberException.php create mode 100644 src/Exceptions/InvalidOKSMCodeException.php create mode 100644 src/Exceptions/NegativeItemPriceException.php create mode 100644 src/Exceptions/NegativeItemQuantityException.php create mode 100644 src/Exceptions/TooHighItemQuantityException.php create mode 100644 src/Exceptions/TooHighSumException.php rename src/Exceptions/{TooLongUnitException.php => TooLongMeasurementUnitException.php} (93%) create mode 100644 tests/AtolOnline/Tests/Entities/ItemTest.php delete mode 100644 tests/ItemTest_todo.php diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index 2c17544..e3c1350 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -102,6 +102,20 @@ final class Constraints */ 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 @@ -122,7 +136,7 @@ final class Constraints const MAX_LENGTH_CASHIER_NAME = 64; /** - * Регулярное выражание для валидации строки ИНН + * Регулярное выражение для валидации строки ИНН * * @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/client/inn" */ @@ -138,9 +152,15 @@ final class Constraints '/^([^\s\\\]{0,17}|\+[^\s\\\]{1,18})$/'; /** - * Регулярное выражание для валидации строки Callback URL + * Регулярное выражение для валидации строки 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}$/'; } diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index 7b73a6d..734cca4 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -145,5 +145,5 @@ final class Ffd105Tags /** * Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155) */ - const DECLARATION_NUMBER = 1231; + const ITEM_DECLARATION_NUMBER = 1231; } diff --git a/src/Entities/Client.php b/src/Entities/Client.php index 3d8376e..e0672c1 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -15,14 +15,15 @@ use AtolOnline\{ Constants\Constraints, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, + Exceptions\InvalidPhoneException, Exceptions\TooLongClientContactException, Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException}; /** - * Класс Client, описывающий сущность покупателя + * Класс, описывающий покупателя * - * @package AtolOnline\Entities + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ class Client extends Entity { @@ -84,7 +85,6 @@ class Client extends Entity /** * Устанавливает наименование покупателя * - * @todo улучшить валидацию по Constraints::PATTERN_PHONE * @param string|null $name * @return $this * @throws TooLongClientNameException @@ -148,14 +148,14 @@ class Client extends Entity * * @param string|null $phone Номер телефона * @return $this - * @throws TooLongClientContactException + * @throws InvalidPhoneException */ public function setPhone(?string $phone): self { if (is_string($phone)) { $phone = preg_replace('/[^\d]/', '', trim($phone)); - if (mb_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_CONTACT) { - throw new TooLongClientContactException($phone); + if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { + throw new InvalidPhoneException($phone); } } $this->phone = empty($phone) ? null : "+$phone"; diff --git a/src/Entities/Document.php b/src/Entities/Document.php index 4bc4e7c..8c88432 100644 --- a/src/Entities/Document.php +++ b/src/Entities/Document.php @@ -21,8 +21,8 @@ use AtolOnline\Exceptions\TooLongCashierException; use AtolOnline\Exceptions\TooLongClientContactException; use AtolOnline\Exceptions\TooLongEmailException; use AtolOnline\Exceptions\TooLongItemNameException; +use AtolOnline\Exceptions\TooLongMeasurementUnitException; use AtolOnline\Exceptions\TooLongPaymentAddressException; -use AtolOnline\Exceptions\TooLongUnitException; use AtolOnline\Exceptions\TooLongUserdataException; use AtolOnline\Exceptions\TooManyException; use AtolOnline\Exceptions\TooManyItemsException; @@ -351,7 +351,7 @@ class Document extends Entity * @throws TooManyException * @throws TooManyItemsException * @throws TooManyPaymentsException - * @throws TooLongUnitException + * @throws TooLongMeasurementUnitException * @throws TooLongUserdataException * @throws Exception */ diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 9421193..8e7b226 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -13,9 +13,20 @@ namespace AtolOnline\Entities; use AtolOnline\{ Constants\Constraints, + Enums\PaymentMethods, + Enums\PaymentObjects, + Enums\VatTypes, + Exceptions\EmptyItemNameException, + Exceptions\InvalidDeclarationNumberException, + Exceptions\InvalidEnumValueException, + Exceptions\InvalidOKSMCodeException, + Exceptions\NegativeItemPriceException, + Exceptions\NegativeItemQuantityException, + Exceptions\TooHighItemQuantityException, Exceptions\TooHighPriceException, + Exceptions\TooHighSumException, Exceptions\TooLongItemNameException, - Exceptions\TooLongUnitException, + Exceptions\TooLongMeasurementUnitException, Exceptions\TooLongUserdataException, Exceptions\TooManyException}; @@ -32,94 +43,85 @@ class Item extends Entity protected string $name; /** - * @var int Цена в копейках (с учётом скидок и наценок) (1079) + * @var float Цена в рублях (с учётом скидок и наценок) (1079) */ - protected int $price = 0; + protected float $price; /** - * @var float Количество, вес (1023) + * @var float Количество/вес (1023) */ - protected float $quantity = 0.0; + protected float $quantity; /** - * @var float Сумма в копейках (1043) + * @var string|null Единица измерения (1197) */ - protected float $sum = 0; + protected ?string $measurement_unit = null; /** - * @var string Единица измерения количества (1197) + * @var string|null Признак способа расчёта (1214) */ - protected string $measurement_unit; + 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; + protected ?Vat $vat = null; /** - * @var string Признак способа расчёта (1214) + * @var AgentInfo|null Атрибуты агента */ - protected string $payment_method; + protected ?AgentInfo $agent_info = null; /** - * @var string Признак объекта расчёта (1212) + * @var Supplier|null Атрибуты поставшика */ - protected string $payment_object; + protected ?Supplier $supplier = null; /** - * @var string Дополнительный реквизит (1191) + * @var string|null Дополнительный реквизит (1191) */ - protected string $user_data; + protected ?string $user_data = null; /** - * Item constructor. + * @var string|null Цифровой код страны происхождения товара (1230) + */ + protected ?string $country_code = null; + + /** + * Конструктор * * @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 TooLongItemNameException Слишком длинное наименование - * @throws TooHighPriceException Слишком высокая цена за одну единицу - * @throws TooManyException Слишком большое количество - * @throws TooLongUnitException Слишком длинное название единицы измерения + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException */ public function __construct( - ?string $name = null, - ?float $price = null, - ?float $quantity = null, - ?string $measurement_unit = null, - ?string $vat_type = null, - ?string $payment_object = null, - ?string $payment_method = null + string $name = null, + float $price = null, + float $quantity = 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); - } + !is_null($name) && $this->setName($name); + !is_null($price) && $this->setPrice($price); + !is_null($quantity) && $this->setQuantity($quantity); } /** - * Возвращает наименование. Тег ФФД - 1030. + * Возвращает наименование * * @return string */ @@ -129,51 +131,59 @@ class Item extends Entity } /** - * Устаналивает наименование. Тег ФФД - 1030. + * Устаналивает наименование * * @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, Constraints::MAX_LENGTH_ITEM_NAME); + throw new TooLongItemNameException($name); + } + if (empty($name)) { + throw new EmptyItemNameException(); } $this->name = $name; return $this; } /** - * Возвращает цену в рублях. Тег ФФД - 1079. + * Возвращает цену в рублях * * @return float */ public function getPrice(): float { - return rubles($this->price); + return $this->price; } /** - * Устанавливает цену в рублях. Тег ФФД - 1079. + * Устанавливает цену в рублях * - * @param float $rubles Цена за одну единицу в рублях + * @param float $rubles * @return $this - * @throws TooHighPriceException Слишком высокая цена за одну единицу + * @throws NegativeItemPriceException + * @throws TooHighPriceException */ - public function setPrice(float $rubles): Item + public function setPrice(float $rubles): self { - if ($rubles > 42949672.95) { - throw new TooHighPriceException($rubles, 42949672.95); + if ($rubles > Constraints::MAX_COUNT_ITEM_PRICE) { + throw new TooHighPriceException($this->getName(), $rubles); } - $this->price = kopeks($rubles); - $this->calcSum(); + if ($rubles < 0) { + throw new NegativeItemPriceException($this->getName(), $rubles); + } + $this->price = $rubles; + //$this->calcSum(); return $this; } /** - * Возвращает количество. Тег ФФД - 1023. + * Возвращает количество * * @return float */ @@ -183,99 +193,115 @@ class Item extends Entity } /** - * Устанавливает количество. Тег ФФД - 1023. + * Устанавливает количество * * @param float $quantity Количество - * @param string|null $measurement_unit Единица измерения количества * @return $this - * @throws TooManyException Слишком большое количество - * @throws TooHighPriceException Слишком высокая общая стоимость - * @throws TooLongUnitException Слишком длинное название единицы измерения + * @throws TooHighItemQuantityException + * @throws NegativeItemQuantityException */ - public function setQuantity(float $quantity, string $measurement_unit = null): Item + public function setQuantity(float $quantity): self { $quantity = round($quantity, 3); - if ($quantity > 99999.999) { - throw new TooManyException($quantity, 99999.999); + 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->calcSum(); - if ($measurement_unit) { - $this->setMeasurementUnit($measurement_unit); - } return $this; } /** - * Возвращает заданную единицу измерения количества. Тег ФФД - 1197. + * Возвращает стоимость * - * @return string + * @return float + * @throws TooHighSumException */ - public function getMeasurementUnit(): string + public function getSum(): float + { + $sum = $this->price * $this->quantity; + if ($sum > Constraints::MAX_COUNT_ITEM_PRICE) { + throw new TooHighSumException($this->getName(), $sum); + } + return $sum; + } + + /** + * Возвращает заданную единицу измерения количества + * + * @return string|null + */ + public function getMeasurementUnit(): ?string { return $this->measurement_unit; } /** - * Устанавливает единицу измерения количества. Тег ФФД - 1197. + * Устанавливает единицу измерения количества * - * @param string $measurement_unit Единица измерения количества + * @param string|null $measurement_unit * @return $this - * @throws TooLongUnitException Слишком длинное название единицы измерения + * @throws TooLongMeasurementUnitException */ - public function setMeasurementUnit(string $measurement_unit): Item + public function setMeasurementUnit(?string $measurement_unit): self { - $measurement_unit = trim($measurement_unit); + $measurement_unit = trim((string)$measurement_unit); if (mb_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) { - throw new TooLongUnitException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT); + throw new TooLongMeasurementUnitException($measurement_unit); } - $this->measurement_unit = $measurement_unit; + $this->measurement_unit = $measurement_unit ?: null; return $this; } /** - * Возвращает признак способа оплаты. Тег ФФД - 1214. + * Возвращает признак способа оплаты * - * @return string + * @return string|null */ - public function getPaymentMethod(): string + public function getPaymentMethod(): ?string { return $this->payment_method; } /** - * Устанавливает признак способа оплаты. Тег ФФД - 1214. + * Устанавливает признак способа оплаты * - * @param string $payment_method Признак способа оплаты + * @param string|null $payment_method Признак способа оплаты * @return $this - * @todo Проверка допустимых значений + * @throws InvalidEnumValueException */ - public function setPaymentMethod(string $payment_method): Item + public function setPaymentMethod(?string $payment_method): self { - $this->payment_method = trim($payment_method); + $payment_method = trim((string)$payment_method); + PaymentMethods::isValid($payment_method); + $this->payment_method = $payment_method ?: null; return $this; } /** - * Возвращает признак предмета расчёта. Тег ФФД - 1212. + * Возвращает признак предмета расчёта * - * @return string + * @return string|null */ - public function getPaymentObject(): string + public function getPaymentObject(): ?string { return $this->payment_object; } /** - * Устанавливает признак предмета расчёта. Тег ФФД - 1212. + * Устанавливает признак предмета расчёта * - * @param string $payment_object Признак предмета расчёта + * @param string|null $payment_object Признак предмета расчёта * @return $this - * @todo Проверка допустимых значений + * @throws InvalidEnumValueException */ - public function setPaymentObject(string $payment_object): Item + public function setPaymentObject(?string $payment_object): self { - $this->payment_object = trim($payment_object); + $payment_object = trim((string)$payment_object); + PaymentObjects::isValid($payment_object); + $this->payment_object = $payment_object ?: null; return $this; } @@ -292,25 +318,68 @@ class Item extends Entity /** * Устанавливает ставку НДС * - * @param string|null $vat_type Тип ставки НДС. Передать null, чтобы удалить ставку. + * @param Vat|string|null $vat Объект ставки, одно из значений VatTypes или null для удаления ставки * @return $this - * @throws TooHighPriceException + * @throws TooHighSumException */ - public function setVatType(?string $vat_type): Item + public function setVat(Vat|string|null $vat): self { - if ($vat_type) { - $this->vat - ? $this->vat->setType($vat_type) - : $this->vat = new Vat($vat_type); - } else { - $this->vat = null; + if (is_string($vat)) { + $vat = trim($vat); + VatTypes::isValid($vat) && $vat = new Vat($vat, $this->getSum()); + } elseif ($vat instanceof Vat) { + $vat->setSum($this->getSum()); } - $this->calcSum(); + $this->vat = $vat ?: null; return $this; } /** - * Возвращает дополнительный реквизит. Тег ФФД - 1191. + * Возвращает установленный объект атрибутов агента + * + * @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 */ @@ -320,30 +389,83 @@ class Item extends Entity } /** - * Устанавливает дополнительный реквизит. Тег ФФД - 1191. + * Устанавливает дополнительный реквизит * - * @param string $user_data Дополнительный реквизит. Тег ФФД - 1191. + * @param string|null $user_data Дополнительный реквизит * @return $this * @throws TooLongUserdataException Слишком длинный дополнительный реквизит */ - public function setUserData(string $user_data): self + public function setUserData(?string $user_data): self { - $user_data = trim($user_data); + $user_data = trim((string)$user_data); if (mb_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) { - throw new TooLongUserdataException($user_data, Constraints::MAX_LENGTH_USER_DATA); + throw new TooLongUserdataException($user_data); } - $this->user_data = $user_data; + $this->user_data = $user_data ?: null; return $this; } /** - * Возвращает стоимость. Тег ФФД - 1043. + * Возвращает установленный код страны происхождения товара * - * @return float + * @return string|null + * @see https://ru.wikipedia.org/wiki/Общероссийский_классификатор_стран_мира + * @see https://classifikators.ru/oksm */ - public function getSum(): float + public function getCountryCode(): ?string { - return rubles($this->sum); + 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; } /** @@ -352,45 +474,42 @@ class Item extends Entity * @return float * @throws TooHighPriceException Слишком большая сумма */ - public function calcSum(): float - { - $sum = $this->quantity * $this->price; - if (rubles($sum) > 42949672.95) { - throw new TooHighPriceException($sum, 42949672.95); - } - $this->sum = $sum; - if ($this->vat) { - $this->vat->setSum(rubles($sum)); - } - return $this->getSum(); - } + //public function calcSum(): float + //{ + // $sum = $this->quantity * $this->price; + // if (rubles($sum) > 42949672.95) { + // throw new TooHighPriceException($sum, 42949672.95); + // } + // $this->sum = $sum; + // if ($this->vat) { + // $this->vat->setSum(rubles($sum)); + // } + // return $this->getSum(); + //} /** * @inheritDoc + * @throws TooHighSumException */ - public function jsonSerialize() + public function jsonSerialize(): array { $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 + 'name' => $this->getName(), + 'price' => $this->getPrice(), + 'quantity' => $this->getQuantity(), + 'sum' => $this->getSum(), ]; - if ($this->getVat()) { - $json['vat'] = $this->getVat()->jsonSerialize(); - } - if ($this->getUserData()) { - $json['user_data'] = $this->getUserData(); - } + $this->getMeasurementUnit() && $json['measurement_unit'] = $this->getMeasurementUnit(); + $this->getPaymentMethod() && $json['payment_method'] = $this->getPaymentMethod(); + $this->getPaymentObject() && $json['payment_object'] = $this->getPaymentObject(); + $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(); + $this->getUserData() && $json['user_data'] = $this->getUserData(); + //TODO excise + $this->getCountryCode() && $json['country_code'] = $this->getCountryCode(); + //TODO nomenclature_code return $json; } } diff --git a/src/Exceptions/AtolException.php b/src/Exceptions/AtolException.php index d3e9237..6b86648 100644 --- a/src/Exceptions/AtolException.php +++ b/src/Exceptions/AtolException.php @@ -31,8 +31,9 @@ class AtolException extends Exception */ public function __construct(string $message = '', array $ffd_tags = []) { + $tags = implode(', ', $ffd_tags ?: $this->ffd_tags); parent::__construct( - ($message ?: $this->message) . ' [Теги ФФД: ' . implode(', ', $ffd_tags ?: $this->ffd_tags) . ']' + ($message ?: $this->message) . ($tags ? ' [Теги ФФД: ' . $tags : '') . ']' ); } } diff --git a/src/Exceptions/EmptyItemNameException.php b/src/Exceptions/EmptyItemNameException.php new file mode 100644 index 0000000..30bbfd5 --- /dev/null +++ b/src/Exceptions/EmptyItemNameException.php @@ -0,0 +1,25 @@ +message) . ': ' . $value; - if ($max > 0 || $this->max > 0) { - $message .= ' (макс. = ' . ($max ?? $this->max) . ', фактически = ' . $value . ')'; - } - parent::__construct($message); + parent::__construct( + ($message ?: $this->message) . (((float)$max > 0 || (float)$this->max > 0) ? + ' (макс = ' . ($max ?? $this->max) . ', фактически = ' . $value . ')' : '') + ); } } diff --git a/tests/AtolOnline/Tests/Entities/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php index c2b4c34..b498433 100644 --- a/tests/AtolOnline/Tests/Entities/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -13,7 +13,7 @@ use AtolOnline\{ Entities\Client, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, - Exceptions\TooLongClientContactException, + Exceptions\InvalidPhoneException, Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException, Helpers, @@ -118,7 +118,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Entities\Client::getPhone - * @throws TooLongClientContactException + * @throws InvalidPhoneException */ public function testNullablePhones(mixed $phone): void { @@ -133,7 +133,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Entities\Client::getPhone - * @throws TooLongClientContactException + * @throws InvalidPhoneException */ public function testValidPhone(string $input, string $output): void { @@ -146,13 +146,13 @@ class ClientTest extends BasicTestCase * @todo актуализировать при доработатанной валидации * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone - * @covers \AtolOnline\Exceptions\TooLongClientContactException - * @throws TooLongClientContactException + * @covers \AtolOnline\Exceptions\InvalidPhoneException + * @throws InvalidPhoneException */ - public function testTooLongClientPhone(): void + public function testInvalidPhoneException(): void { - $this->expectException(TooLongClientContactException::class); - (new Client())->setPhone('99999999999999999999999999999999999999999999999999999999999999999999999999'); + $this->expectException(InvalidPhoneException::class); + (new Client())->setPhone(Helpers::randomStr(500)); } /** diff --git a/tests/AtolOnline/Tests/Entities/CompanyTest.php b/tests/AtolOnline/Tests/Entities/CompanyTest.php index b392678..2ed59ef 100644 --- a/tests/AtolOnline/Tests/Entities/CompanyTest.php +++ b/tests/AtolOnline/Tests/Entities/CompanyTest.php @@ -19,11 +19,10 @@ use AtolOnline\{ Exceptions\TooLongEmailException, Exceptions\TooLongPaymentAddressException, Helpers, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; /** - * Набор тестов для проверки работы класс продавца + * Набор тестов для проверки работы класса продавца */ class CompanyTest extends BasicTestCase { diff --git a/tests/AtolOnline/Tests/Entities/ItemTest.php b/tests/AtolOnline/Tests/Entities/ItemTest.php new file mode 100644 index 0000000..4de59d0 --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/ItemTest.php @@ -0,0 +1,659 @@ +assertAtolable( + new Item('test item', 2, 3), + [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + ] + ); + } + + /** + * Тестирует выброс исключения при установке слишком длинного имени предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setName + * @covers \AtolOnline\Exceptions\TooLongItemNameException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + */ + public function testTooLongItemNameException(): void + { + $this->expectException(TooLongItemNameException::class); + new Item(Helpers::randomStr(Constraints::MAX_LENGTH_ITEM_NAME + 1), 2, 3); + } + + /** + * Тестирует выброс исключения при установке пустого имени предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setName + * @covers \AtolOnline\Exceptions\EmptyItemNameException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + */ + public function testEmptyItemNameException(): void + { + $this->expectException(EmptyItemNameException::class); + new Item(" \n\r\t\0 ", 2, 3); + } + + /** + * Тестирует выброс исключения при установке слишком высокой цены предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setPrice + * @covers \AtolOnline\Exceptions\TooHighPriceException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + */ + public function testTooHighPriceException(): void + { + $this->expectException(TooHighPriceException::class); + new Item('test', Constraints::MAX_COUNT_ITEM_PRICE + 0.1, 3); + } + + /** + * Тестирует выброс исключения при получении слишком высокой стоимости предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setPrice + * @covers \AtolOnline\Exceptions\TooHighSumException + * @throws TooHighSumException + */ + public function testTooHighSumException(): void + { + $this->expectException(TooHighSumException::class); + (new Item('test', Constraints::MAX_COUNT_ITEM_PRICE, Constraints::MAX_COUNT_ITEM_QUANTITY))->getSum(); + } + + /** + * Тестирует выброс исключения при установке слишком высокой цены предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setPrice + * @covers \AtolOnline\Exceptions\NegativeItemPriceException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + */ + public function testNegativeItemPriceException(): void + { + $this->expectException(NegativeItemPriceException::class); + new Item('test', -0.01, 3); + } + + /** + * Тестирует выброс исключения при установке слишком большого количества предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setQuantity + * @covers \AtolOnline\Exceptions\TooHighItemQuantityException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + */ + public function testTooHighItemQuantityException(): void + { + $this->expectException(TooHighItemQuantityException::class); + new Item('test', 2, Constraints::MAX_COUNT_ITEM_QUANTITY + 1); + } + + /** + * Тестирует выброс исключения при установке отрицательного количества предмета расчёта + * + * @covers \AtolOnline\Entities\Item + * @covers \AtolOnline\Entities\Item::setQuantity + * @covers \AtolOnline\Exceptions\NegativeItemQuantityException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + */ + public function testNegativeItemQuantityException(): void + { + $this->expectException(NegativeItemQuantityException::class); + new Item('test', 2, -0.01); + } + + /** + * Тестирует установку пустой единицы измерения + * + * @param mixed $param + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Item::setMeasurementUnit + * @covers \AtolOnline\Entities\Item::getMeasurementUnit + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooLongMeasurementUnitException + * @throws TooManyException + * @throws NegativeItemQuantityException + */ + public function testNullableMeasurementUnit(mixed $param): void + { + $this->assertNull((new Item('test item', 2, 3))->setMeasurementUnit($param)->getMeasurementUnit()); + } + + /** + * Тестирует выброс исключения при установке слишком длинной единицы измерения + * + * @covers \AtolOnline\Entities\Item::setMeasurementUnit + * @covers \AtolOnline\Exceptions\TooLongMeasurementUnitException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooLongMeasurementUnitException + * @throws TooManyException + */ + public function testTooLongMeasurementUnitException(): void + { + $this->expectException(TooLongMeasurementUnitException::class); + (new Item('test item', 2, 3)) + ->setMeasurementUnit(Helpers::randomStr(Constraints::MAX_LENGTH_MEASUREMENT_UNIT + 1)); + } + + /** + * Тестирует сеттеры-геттеры валидных перечислимых значений атрибутов + * + * @covers \AtolOnline\Entities\Item::setPaymentMethod + * @covers \AtolOnline\Entities\Item::getPaymentMethod + * @covers \AtolOnline\Entities\Item::setPaymentObject + * @covers \AtolOnline\Entities\Item::getPaymentObject + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws InvalidEnumValueException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws TooHighPriceException + * @throws NegativeItemQuantityException + * @throws TooLongItemNameException + */ + public function testValidEnums(): void + { + $item = new Item('test item', 2, 3); + $this->assertEquals( + PaymentMethods::ADVANCE, + $item->setPaymentMethod(PaymentMethods::ADVANCE)->getPaymentMethod() + ); + $this->assertEquals( + PaymentObjects::COMMODITY, + $item->setPaymentObject(PaymentObjects::COMMODITY)->getPaymentObject() + ); + $this->assertAtolable($item, [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'payment_method' => 'advance', + 'payment_object' => 'commodity', + ]); + } + + /** + * Тестирует установку невалидного способа оплаты + * + * @covers \AtolOnline\Entities\Item::setPaymentMethod + * @covers \AtolOnline\Exceptions\InvalidEnumValueException + * @throws EmptyItemNameException + * @throws InvalidEnumValueException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws TooHighPriceException + * @throws NegativeItemQuantityException + * @throws TooLongItemNameException + */ + public function testInvalidPaymentMethod(): void + { + $this->expectException(InvalidEnumValueException::class); + $this->expectExceptionMessage('Некорректное значение AtolOnline\Enums\PaymentMethods::wrong_value'); + (new Item('test item', 2, 3))->setPaymentMethod('wrong_value'); + } + + /** + * Тестирует установку невалидного предмета расчёта + * + * @covers \AtolOnline\Entities\Item::setPaymentObject + * @covers \AtolOnline\Exceptions\InvalidEnumValueException + * @throws EmptyItemNameException + * @throws InvalidEnumValueException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws TooHighPriceException + * @throws NegativeItemQuantityException + * @throws TooLongItemNameException + */ + public function testInvalidPaymentObject(): void + { + $this->expectException(InvalidEnumValueException::class); + $this->expectExceptionMessage('Некорректное значение AtolOnline\Enums\PaymentObjects::wrong_value'); + (new Item('test item', 2, 3))->setPaymentObject('wrong_value'); + } + + /** + * Тестирует установку ставки НДС по строковой константе типа ставки + * + * @covers \AtolOnline\Entities\Item::setVat + * @covers \AtolOnline\Entities\Item::getVat + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testValidVatByString(): void + { + $item = (new Item('test item', 2, 3))->setVat(VatTypes::VAT20); + $this->assertIsSameClass(Vat::class, $item->getVat()); + $this->assertEquals(VatTypes::VAT20, $item->getVat()->getType()); + $this->assertEquals($item->getSum(), $item->getVat()->getSum()); + $this->assertAtolable($item, [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'vat' => [ + 'type' => 'vat20', + 'sum' => 1.2, + ], + ]); + } + + /** + * Тестирует установку ставки НДС объектом + * + * @covers \AtolOnline\Entities\Item::setVat + * @covers \AtolOnline\Entities\Item::getVat + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testValidVatByObject(): void + { + $vat = new Vat(VatTypes::VAT20, 4000); + $item = (new Item('test item', 2, 3))->setVat($vat); + $this->assertIsSameClass(Vat::class, $item->getVat()); + $this->assertEquals(VatTypes::VAT20, $item->getVat()->getType()); + $this->assertEquals($item->getSum(), $item->getVat()->getSum()); + $this->assertAtolable($item, [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'vat' => [ + 'type' => 'vat20', + 'sum' => 1.2, + ], + ]); + } + + /** + * Тестирует обнуление ставки НДС + * + * @param mixed $vat + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Item::setVat + * @covers \AtolOnline\Entities\Item::getVat + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testNullableVatByString(mixed $vat): void + { + $item = (new Item('test item', 2, 3))->setVat($vat); + $this->assertNull($item->getVat()); + } + + /** + * Тестирует установку атрибутов агента + * + * @covers \AtolOnline\Entities\Item::setAgentInfo + * @covers \AtolOnline\Entities\Item::getAgentInfo + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPhoneException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooLongPayingAgentOperationException + * @throws TooManyException + */ + public function testAgentInfo(): void + { + $agent_info = new AgentInfo( + AgentTypes::ANOTHER, + new PayingAgent('test', ['+79518888888']), + new ReceivePaymentsOperator(['+79519999999']), + new MoneyTransferOperator('MTO Name', '9876543210', 'London', ['+79517777777']), + ); + $item = (new Item('test item', 2, 3))->setAgentInfo($agent_info); + $this->assertEquals($agent_info, $item->getAgentInfo()); + } + + /** + * Тестирует установку поставщика + * + * @covers \AtolOnline\Entities\Item::setSupplier + * @covers \AtolOnline\Entities\Item::getSupplier + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws InvalidInnLengthException + * @throws InvalidPhoneException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testSupplier(): void + { + $supplier = new Supplier( + 'some name', + '+fasd3\qe3fs_=nac99013928czc', + ['+122997365456'], + ); + $item = (new Item('test item', 2, 3))->setSupplier($supplier); + $this->assertEquals($supplier, $item->getSupplier()); + $this->assertAtolable($item, [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'supplier_info' => [ + 'name' => 'some name', + 'inn' => '3399013928', + 'phones' => ['+122997365456'], + ], + ]); + } + + /** + * Тестирует установку валидных пользовательских данных + * + * @covers \AtolOnline\Entities\Item::setUserData + * @covers \AtolOnline\Entities\Item::getUserData + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooLongUserdataException + * @throws TooManyException + */ + public function testValidUserdata(): void + { + $this->assertAtolable( + (new Item('test item', 2, 3)) + ->setUserData($user_data = Helpers::randomStr(Constraints::MAX_LENGTH_USER_DATA)), + [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'user_data' => $user_data, + ] + ); + } + + /** + * Тестирует установку пустых пользовательских данных + * + * @param mixed $param + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Item::setUserData + * @covers \AtolOnline\Entities\Item::getUserData + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws NegativeItemQuantityException + * @throws TooLongUserdataException + */ + public function testNullableUserData(mixed $param): void + { + $item = new Item('test item', 2, 3); + $this->assertNull($item->setUserData($param)->getUserData()); + } + + /** + * Тестирует выброс исключения при установке слишком длинных польз. данных + * + * @covers \AtolOnline\Entities\Item::setUserData + * @covers \AtolOnline\Exceptions\TooLongUserdataException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooLongUserdataException + * @throws TooManyException + */ + public function testTooLongUserdataException(): void + { + $this->expectException(TooLongUserdataException::class); + (new Item('test item', 2, 3))->setUserData(Helpers::randomStr(Constraints::MAX_LENGTH_USER_DATA + 1)); + } + + /** + * Тестирует установку кода страны происхождения товара + * + * @covers \AtolOnline\Entities\Item::setCountryCode + * @covers \AtolOnline\Entities\Item::getCountryCode + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws InvalidOKSMCodeException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testCountryCode(): void + { + $this->assertAtolable( + (new Item('test item', 2, 3))->setCountryCode('800'), + [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'country_code' => '800', + ] + ); + } + + /** + * Тестирует выброс исключения при установке невалидного кода страны происхождения товара + * + * @covers \AtolOnline\Entities\Item::setCountryCode + * @covers \AtolOnline\Exceptions\InvalidOKSMCodeException + * @throws EmptyItemNameException + * @throws InvalidOKSMCodeException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testInvalidOKSMCodeException(): void + { + $this->expectException(InvalidOKSMCodeException::class); + (new Item('test item', 2, 3))->setCountryCode(Helpers::randomStr()); + } + + /** + * Тестирует установку валидного кода таможенной декларации + * + * @covers \AtolOnline\Entities\Item::getDeclarationNumber + * @covers \AtolOnline\Entities\Item::setDeclarationNumber + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws InvalidDeclarationNumberException + */ + public function testValidDeclarationNumber(): void + { + $this->assertAtolable( + (new Item('test item', 2, 3)) + ->setDeclarationNumber($code = Helpers::randomStr()), + [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'declaration_number' => $code, + ] + ); + } + + /** + * Тестирует выброс исключения при установке слишком короткого кода таможенной декларации + * + * @covers \AtolOnline\Entities\Item::setDeclarationNumber + * @covers \AtolOnline\Exceptions\InvalidDeclarationNumberException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws InvalidDeclarationNumberException + * @throws TooManyException + */ + public function testInvalidDeclarationNumberExceptionMin(): void + { + $this->expectException(InvalidDeclarationNumberException::class); + (new Item('test item', 2, 3)) + ->setDeclarationNumber(Helpers::randomStr(Constraints::MIN_LENGTH_DECLARATION_NUMBER - 1)); + } + + /** + * Тестирует выброс исключения при установке слишком длинного кода таможенной декларации + * + * @covers \AtolOnline\Entities\Item::setDeclarationNumber + * @covers \AtolOnline\Exceptions\InvalidDeclarationNumberException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws InvalidDeclarationNumberException + * @throws TooManyException + */ + public function testInvalidDeclarationNumberExceptionMax(): void + { + $this->expectException(InvalidDeclarationNumberException::class); + (new Item('test item', 2, 3)) + ->setDeclarationNumber(Helpers::randomStr(Constraints::MAX_LENGTH_DECLARATION_NUMBER + 1)); + } +} diff --git a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php index 0be356d..bf6560a 100644 --- a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php +++ b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php @@ -16,7 +16,7 @@ use AtolOnline\{ Tests\BasicTestCase}; /** - * Набор тестов для проверки работы класса поставщика + * Набор тестов для проверки работы класса оператора перевода */ class MoneyTransferOperatorTest extends BasicTestCase { diff --git a/tests/ItemTest_todo.php b/tests/ItemTest_todo.php deleted file mode 100644 index 97d3f3f..0000000 --- a/tests/ItemTest_todo.php +++ /dev/null @@ -1,163 +0,0 @@ -assertAtolable($item); - $this->assertEquals('Банан', $item->getName()); - $this->assertEquals(65.99, $item->getPrice()); - $this->assertEquals(2.74, $item->getQuantity()); - $this->assertEquals('кг', $item->getMeasurementUnit()); - $this->assertEquals(VatTypes::NONE, $item->getVat()->getType()); - $this->assertEquals(PaymentObjects::COMMODITY, $item->getPaymentObject()); - $this->assertEquals(PaymentMethods::FULL_PAYMENT, $item->getPaymentMethod()); - } - - /** - * Тестирует установку параметров через сеттеры - * - * @throws AtolOnline\Exceptions\TooLongNameException - * @throws AtolOnline\Exceptions\TooHighPriceException - * @throws AtolOnline\Exceptions\BasicTooManyException - * @throws AtolOnline\Exceptions\TooLongUnitException - * @throws AtolOnline\Exceptions\TooLongUserdataException - */ - public function testSetters() - { - $item = new Item(); - $item->setName('Банан'); - $item->setPrice(65.99); - $item->setQuantity(2.74); - $item->setMeasurementUnit('кг'); - $item->setVatType(VatTypes::NONE); - $item->setPaymentObject(PaymentObjects::COMMODITY); - $item->setPaymentMethod(PaymentMethods::FULL_PAYMENT); - $item->setUserData('Some user data'); - $this->assertAtolable($item); - $this->assertEquals('Банан', $item->getName()); - $this->assertEquals(65.99, $item->getPrice()); - $this->assertEquals(2.74, $item->getQuantity()); - $this->assertEquals('кг', $item->getMeasurementUnit()); - $this->assertEquals(VatTypes::NONE, $item->getVat()->getType()); - $this->assertEquals(PaymentObjects::COMMODITY, $item->getPaymentObject()); - $this->assertEquals(PaymentMethods::FULL_PAYMENT, $item->getPaymentMethod()); - $this->assertEquals('Some user data', $item->getUserData()); - } - - /** - * Тестирует установку ставки НДС разными путями - * - * @throws TooHighPriceException - */ - public function testSetVat() - { - $item = new Item(); - $item->setVatType(VatTypes::NONE); - $this->assertEquals(VatTypes::NONE, $item->getVat()->getType()); - $item->setVatType(VatTypes::VAT20); - $this->assertEquals(VatTypes::VAT20, $item->getVat()->getType()); - } - - /** - * Тестирует исключение о слишком длинном наименовании - * - * @throws TooLongItemNameException - */ - public function testAtolNameTooLongException() - { - $item = new Item(); - $this->expectException(TooLongItemNameException::class); - $item->setName(Helpers::randomStr(130)); - } - - /** - * Тестирует исключение о слишком высоком количестве - * - * @throws TooHighPriceException - * @throws TooManyException - * @throws TooLongUnitException - */ - public function testAtolQuantityTooHighException() - { - $item = new Item(); - $this->expectException(TooManyException::class); - $item->setQuantity(100000.1); - } - - /** - * Тестирует исключение о слишком высокой цене - * - * @throws TooHighPriceException - */ - public function testAtolPriceTooHighException() - { - $item = new Item(); - $this->expectException(TooHighPriceException::class); - $item->setPrice(42949673.1); - } - - /** - * Тестирует исключение о слишком длинных польз. данных - * - * @throws TooLongUserdataException - */ - public function testAtolUserdataTooLongException() - { - $item = new Item(); - $this->expectException(TooLongUserdataException::class); - $item->setUserData('User data User data User data User data User data User data User data'); - } - - /** - * Тестирует исключение о слишком длинной единице измерения - * - * @throws TooLongUnitException - */ - public function testAtolUnitTooLongException() - { - $item = new Item(); - $this->expectException(TooLongUnitException::class); - $item->setMeasurementUnit('кг кг кг кг кг кг кг кг кг '); - } -} From 3f3eb68ea2b2f29803c18c85f942907e0f199ca0 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 1 Dec 2021 20:11:57 +0800 Subject: [PATCH 37/85] =?UTF-8?q?=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=B2=20composer.?= =?UTF-8?q?json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index e51c624..ca6be48 100644 --- a/composer.json +++ b/composer.json @@ -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", From 650b46923eb56faba9393935bdeaaf4cf5145d46 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 1 Dec 2021 20:12:11 +0800 Subject: [PATCH 38/85] =?UTF-8?q?=D0=91=D0=B5=D0=B9=D0=B4=D0=B6=D0=B8?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B2=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 301019b..c0e144b 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,16 @@ [![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) +[![Total Downloads](http://poser.pugx.org/axenov/atol-online/downloads)](https://packagist.org/packages/axenov/atol-online) +[![License](http://poser.pugx.org/axenov/atol-online/license)](https://packagist.org/packages/axenov/atol-online) Библиотека для фискализации чеков по 54-ФЗ через [облачную ККТ АТОЛ](https://online.atol.ru/). **[Документация](/docs/readme.md)** -Текущие поддерживаемые версии: +Текущие поддерживаемые версии АТОЛ Онлайн: | Протокол | API | ФФД | Статус | |----------|-----|------|-------------| From 11646113b6349f12f3a40441f6343bfbc9ec251b Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 2 Dec 2021 00:23:37 +0800 Subject: [PATCH 39/85] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20`Item`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - поддержка `excise`, покрыта тестами - фикс `setVat()` - улучшен `jsonSerialize()` --- src/Entities/Item.php | 60 +++++++++++++++---- .../NegativeItemExciseException.php | 33 ++++++++++ tests/AtolOnline/Tests/Entities/ItemTest.php | 56 ++++++++++++++++- 3 files changed, 134 insertions(+), 15 deletions(-) create mode 100644 src/Exceptions/NegativeItemExciseException.php diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 8e7b226..aba875d 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -20,6 +20,7 @@ use AtolOnline\{ Exceptions\InvalidDeclarationNumberException, Exceptions\InvalidEnumValueException, Exceptions\InvalidOKSMCodeException, + Exceptions\NegativeItemExciseException, Exceptions\NegativeItemPriceException, Exceptions\NegativeItemQuantityException, Exceptions\TooHighItemQuantityException, @@ -28,7 +29,8 @@ use AtolOnline\{ Exceptions\TooLongItemNameException, Exceptions\TooLongMeasurementUnitException, Exceptions\TooLongUserdataException, - Exceptions\TooManyException}; + Exceptions\TooManyException +}; /** * Предмет расчёта (товар, услуга) @@ -92,6 +94,11 @@ class Item extends Entity */ protected ?string $user_data = null; + /** + * @var float|null Сумма акциза, включенная в стоимость (1229) + */ + protected ?float $excise = null; + /** * @var string|null Цифровой код страны происхождения товара (1230) */ @@ -214,14 +221,14 @@ class Item extends Entity } /** - * Возвращает стоимость + * Возвращает стоимость (цена * количество + акциз) * * @return float * @throws TooHighSumException */ public function getSum(): float { - $sum = $this->price * $this->quantity; + $sum = $this->getPrice() * $this->getQuantity() + (float)$this->getExcise(); if ($sum > Constraints::MAX_COUNT_ITEM_PRICE) { throw new TooHighSumException($this->getName(), $sum); } @@ -321,16 +328,19 @@ class Item extends Entity * @param Vat|string|null $vat Объект ставки, одно из значений VatTypes или null для удаления ставки * @return $this * @throws TooHighSumException + * @throws InvalidEnumValueException */ public function setVat(Vat|string|null $vat): self { if (is_string($vat)) { $vat = trim($vat); - VatTypes::isValid($vat) && $vat = new Vat($vat, $this->getSum()); + 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; } - $this->vat = $vat ?: null; return $this; } @@ -405,6 +415,32 @@ class Item extends Entity return $this; } + /** + * Возвращает установленную сумму акциза + * + * @return float|null + */ + public function getExcise(): ?float + { + return $this->excise; + } + + /** + * Устанавливает сумму акциза + * + * @param float|null $excise + * @return Item + * @throws NegativeItemExciseException + */ + public function setExcise(?float $excise): Item + { + if ($excise < 0) { + throw new NegativeItemExciseException($this->getName(), $excise); + } + $this->excise = $excise; + return $this; + } + /** * Возвращает установленный код страны происхождения товара * @@ -499,16 +535,16 @@ class Item extends Entity 'quantity' => $this->getQuantity(), 'sum' => $this->getSum(), ]; - $this->getMeasurementUnit() && $json['measurement_unit'] = $this->getMeasurementUnit(); - $this->getPaymentMethod() && $json['payment_method'] = $this->getPaymentMethod(); - $this->getPaymentObject() && $json['payment_object'] = $this->getPaymentObject(); - $this->getDeclarationNumber() && $json['declaration_number'] = $this->getDeclarationNumber(); + !is_null($this->getMeasurementUnit()) && $json['measurement_unit'] = $this->getMeasurementUnit(); + !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(); - $this->getUserData() && $json['user_data'] = $this->getUserData(); - //TODO excise - $this->getCountryCode() && $json['country_code'] = $this->getCountryCode(); + !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(); //TODO nomenclature_code return $json; } diff --git a/src/Exceptions/NegativeItemExciseException.php b/src/Exceptions/NegativeItemExciseException.php new file mode 100644 index 0000000..4642a75 --- /dev/null +++ b/src/Exceptions/NegativeItemExciseException.php @@ -0,0 +1,33 @@ +setDeclarationNumber(Helpers::randomStr(Constraints::MAX_LENGTH_DECLARATION_NUMBER + 1)); } + + /** + * Тестирует установку акциза и расчёт суммы с его учётом + * + * @covers \AtolOnline\Entities\Item::setExcise + * @covers \AtolOnline\Entities\Item::getExcise + * @covers \AtolOnline\Entities\Item::getSum + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + * @throws NegativeItemExciseException + */ + public function testExcise(): void + { + $this->assertAtolable( + (new Item('test item', 2, 3))->setExcise(1), + [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 7, + 'excise' => 1, + ] + ); + } + + /** + * Тестирует установку акциза и расчёт суммы с его учётом + * + * @covers \AtolOnline\Entities\Item::setExcise + * @covers \AtolOnline\Exceptions\NegativeItemExciseException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + * @throws NegativeItemExciseException + */ + public function testNegativeItemExciseException(): void + { + $this->expectException(NegativeItemExciseException::class); + (new Item('test item', 2, 3))->setExcise(-1); + } } From cb24bb1fb06e8242d0d70cf566e7da9fd010a924 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 2 Dec 2021 01:10:16 +0800 Subject: [PATCH 40/85] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=B5=D0=BD=D0=B0=D0=BC=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B8=20=D1=82=D0=B5=D0=B3=D0=BE=D0=B2=20=D0=A4=D0=A4=D0=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Ffd105Tags.php | 77 ++++++++++++++++++++++++++++++++++- src/Entities/Vat.php | 2 + src/Enums/AgentTypes.php | 6 ++- src/Enums/CorrectionTypes.php | 4 +- src/Enums/PaymentMethods.php | 4 +- src/Enums/PaymentObjects.php | 4 +- src/Enums/PaymentTypes.php | 12 +++++- src/Enums/SnoTypes.php | 4 +- src/Enums/VatTypes.php | 20 +++++++-- 9 files changed, 121 insertions(+), 12 deletions(-) diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index 734cca4..e7ed2b0 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -39,11 +39,21 @@ final class Ffd105Tags */ const COMPANY_INN = 1008; + /** + * Применяемая система налогообложения + */ + const COMPANY_SNO = 1055; + /** * Место расчётов */ const COMPANY_PADDRESS = 1187; + /** + * Признак агента по предмету расчёта + */ + const AGENT_TYPE = 1222; + /** * Телефон оператора по приёму платежей */ @@ -132,7 +142,7 @@ final class Ffd105Tags /** * Сумма акциза с учётом копеек, включённая в стоимость предмета расчёта */ - const ITEM_PAYMENT_EXSICE = 1229; + const ITEM_EXCISE = 1229; /** * Цифровой код страны происхождения товара в соответствии с Общероссийским классификатором стран мира @@ -146,4 +156,69 @@ final class Ffd105Tags * Номер таможенной декларации (в соотв. с приказом ФНС России от 24.03.2016 N ММВ-7-15/155) */ const ITEM_DECLARATION_NUMBER = 1231; + + /** + * Тип коррекции + */ + const CORRECTION_TYPE = 1173; + + /** + * Сумма по чеку (БСО) наличными + */ + 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; } diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index e133fea..a4438db 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace AtolOnline\Entities; use AtolOnline\Enums\VatTypes; +use AtolOnline\Exceptions\InvalidEnumValueException; use AtolOnline\Helpers; /** @@ -48,6 +49,7 @@ class Vat extends Entity * * @param string $type Тип ставки НДС * @return $this + * @throws InvalidEnumValueException */ public function setType(string $type): self { diff --git a/src/Enums/AgentTypes.php b/src/Enums/AgentTypes.php index ec87b45..8d906dc 100644 --- a/src/Enums/AgentTypes.php +++ b/src/Enums/AgentTypes.php @@ -11,10 +11,12 @@ declare(strict_types = 1); namespace AtolOnline\Enums; +use AtolOnline\Constants\Ffd105Tags; + /** * Константы, определяющие типы агента * - * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 18 + * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 18, 26 */ final class AgentTypes extends Enum { @@ -58,6 +60,6 @@ final class AgentTypes extends Enum */ public static function getFfdTags(): array { - return [1057]; + return [Ffd105Tags::AGENT_TYPE]; } } diff --git a/src/Enums/CorrectionTypes.php b/src/Enums/CorrectionTypes.php index c975898..5c7d19c 100644 --- a/src/Enums/CorrectionTypes.php +++ b/src/Enums/CorrectionTypes.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Enums; +use AtolOnline\Constants\Ffd105Tags; + /** * Константы, определяющие типы документов коррекции * @@ -33,6 +35,6 @@ final class CorrectionTypes extends Enum */ public static function getFfdTags(): array { - return [1173]; + return [Ffd105Tags::CORRECTION_TYPE]; } } diff --git a/src/Enums/PaymentMethods.php b/src/Enums/PaymentMethods.php index 5511c86..7ebc2f2 100644 --- a/src/Enums/PaymentMethods.php +++ b/src/Enums/PaymentMethods.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Enums; +use AtolOnline\Constants\Ffd105Tags; + /** * Константы, определяющие признаки способов расчёта * @@ -58,6 +60,6 @@ final class PaymentMethods extends Enum */ public static function getFfdTags(): array { - return [1214]; + return [Ffd105Tags::ITEM_PAYMENT_METHOD]; } } diff --git a/src/Enums/PaymentObjects.php b/src/Enums/PaymentObjects.php index 9ef16e8..eee6f8c 100644 --- a/src/Enums/PaymentObjects.php +++ b/src/Enums/PaymentObjects.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Enums; +use AtolOnline\Constants\Ffd105Tags; + /** * Константы, определяющие признаки предметов расчёта * @@ -160,6 +162,6 @@ final class PaymentObjects extends Enum */ public static function getFfdTags(): array { - return [1212]; + return [Ffd105Tags::ITEM_PAYMENT_OBJECT]; } } diff --git a/src/Enums/PaymentTypes.php b/src/Enums/PaymentTypes.php index 350baf3..2e658ad 100644 --- a/src/Enums/PaymentTypes.php +++ b/src/Enums/PaymentTypes.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Enums; +use AtolOnline\Constants\Ffd105Tags; + /** * Константы, определяющие виды оплат * @@ -31,7 +33,7 @@ final class PaymentTypes extends Enum /** * Предварительная оплата (зачёт аванса) */ - const PRE_PAID = 2; + const PREPAID = 2; /** * Предварительная оплата (кредит) @@ -78,6 +80,12 @@ final class PaymentTypes extends Enum */ public static function getFfdTags(): array { - return [1031, 1081, 1215, 1216, 1217]; + return [ + Ffd105Tags::PAYMENT_TYPE_CASH, + Ffd105Tags::PAYMENT_TYPE_ELECTRON, + Ffd105Tags::PAYMENT_TYPE_PREPAID, + Ffd105Tags::PAYMENT_TYPE_CREDIT, + Ffd105Tags::PAYMENT_TYPE_OTHER, + ]; } } diff --git a/src/Enums/SnoTypes.php b/src/Enums/SnoTypes.php index 0e551ed..231bf6e 100644 --- a/src/Enums/SnoTypes.php +++ b/src/Enums/SnoTypes.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Enums; +use AtolOnline\Constants\Ffd105Tags; + /** * Константы, определяющие типы налогообложения * @@ -53,6 +55,6 @@ final class SnoTypes extends Enum */ public static function getFfdTags(): array { - return [1055]; + return [Ffd105Tags::COMPANY_SNO]; } } diff --git a/src/Enums/VatTypes.php b/src/Enums/VatTypes.php index f8e377e..27aa2df 100644 --- a/src/Enums/VatTypes.php +++ b/src/Enums/VatTypes.php @@ -11,12 +11,10 @@ declare(strict_types = 1); namespace AtolOnline\Enums; -use MyCLabs\Enum\Enum; +use AtolOnline\Constants\Ffd105Tags; /** * Константы, определяющие типы ставок НДС - * - * Теги ФФД: 1199, 1105, 1104, 1103, 1102, 1107, 1106 */ final class VatTypes extends Enum { @@ -59,4 +57,20 @@ final class VatTypes extends Enum * НДС 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, + ]; + } } From 267431ec28bfdde212da615d348408dab76167e8 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 2 Dec 2021 01:10:54 +0800 Subject: [PATCH 41/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BE=D1=87=D0=B8=20?= =?UTF-8?q?=D0=B2=20readme=20=D0=B8=20phpunit.xml=20(=D1=83=D0=B4=D0=B0?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B4=D0=B5=D1=84=D0=BE=D0=BB=D1=82?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=D1=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- phpunit.xml | 11 ++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c0e144b..be5999e 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ composer test ## Дополнительные ресурсы -* [Документация АТОЛ](https://online.atol.ru/lib/) - **[Документация к библиотеке](/docs/readme.md)** +* **[Документация к библиотеке](/docs/readme.md)** * Telegram-канал: [@atolonline_php](https://t.me/atolonline_php) +* [Документация АТОЛ Онлайн](https://online.atol.ru/lib/) * Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md) ## Лицензия diff --git a/phpunit.xml b/phpunit.xml index 45ebb1c..0c0f959 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -9,15 +9,8 @@ + colors="true"> ./tests @@ -29,4 +22,4 @@ src - \ No newline at end of file + From 96137d20b2b4c8606a9b8b9145d2d59b4c7a5fdd Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 2 Dec 2021 14:34:21 +0800 Subject: [PATCH 42/85] =?UTF-8?q?=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B9=D0=BA=D0=B0=20gh-actions=20=D0=B4=D0=BB=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20codecov.io?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++++++++++++ .github/workflows/dev.yml | 25 ------------------ .github/workflows/master.yml | 25 ------------------ .gitignore | 1 + README.md | 12 ++++++--- composer.json | 2 +- 6 files changed, 59 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/dev.yml delete mode 100644 .github/workflows/master.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..766c2fe --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +# https://github.com/shivammathur/setup-php/blob/master/README.md + +name: CI + +on: + push: + branches: [ master, dev ] + pull_request: + branches: [ master, 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 diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml deleted file mode 100644 index 7c70f41..0000000 --- a/.github/workflows/dev.yml +++ /dev/null @@ -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: 8.0 - only_args: --prefer-dist --no-progress - - - name: Run phpunit tests - uses: php-actions/phpunit@v9 - with: - configuration: ./phpunit.xml diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml deleted file mode 100644 index 0e0fd58..0000000 --- a/.github/workflows/master.yml +++ /dev/null @@ -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 diff --git a/.gitignore b/.gitignore index 5bc0a5c..6ccbbdc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /config.php *cache* /test.php +/.coverage-report diff --git a/README.md b/README.md index be5999e..e218791 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,10 @@ [![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) -[![Total Downloads](http://poser.pugx.org/axenov/atol-online/downloads)](https://packagist.org/packages/axenov/atol-online) +[![codecov](https://codecov.io/gh/anthonyaxenov/atol-online/branch/master/graph/badge.svg?token=WR2IV7FTF0)](https://codecov.io/gh/anthonyaxenov/atol-online) [![License](http://poser.pugx.org/axenov/atol-online/license)](https://packagist.org/packages/axenov/atol-online) + + Библиотека для фискализации чеков по 54-ФЗ через [облачную ККТ АТОЛ](https://online.atol.ru/). @@ -63,12 +64,15 @@ Файлы тестов находятся в директории `/tests` корня репозитория. -Для запуска тестов необходимо перейти в корень вашего проекта и выполнить команду: +Для запуска тестов необходимо перейти в корень репозитория и выполнить одну из команд: ```bash -composer test +composer test # обычное тестирование +composer test-cov # тестирование с покрытием ``` +После тестирования с покрытием создаётся отчёт в директории `.coverage-report` в корне репозитория. + ## Использование библиотеки Весь исходный код находится в директории [`/src`](/src). diff --git a/composer.json b/composer.json index ca6be48..c78cdf8 100644 --- a/composer.json +++ b/composer.json @@ -73,6 +73,6 @@ }, "scripts": { "test": "vendor/bin/phpunit --colors=always", - "test-cov": "vendor/bin/phpunit --coverage-html coverage" + "test-cov": "php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html .coverage-report" } } From 1c0d8ba64d2c25ad40c3181fad3c5a5d3b504c5d Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 2 Dec 2021 16:14:01 +0800 Subject: [PATCH 43/85] =?UTF-8?q?=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e218791..55de7a0 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,25 @@ # АТОЛ Онлайн ---- - -**В этой ветке проводится глубокий рефакторинг, стабилизация и активная подготовка к `v1.0.0`. Документация -актуализируется постепенно.** - ---- - -[![Master build](https://github.com/anthonyaxenov/atol-online/actions/workflows/master.yml/badge.svg)](https://github.com/anthonyaxenov/atol-online/actions/workflows/master.yml) -[![Dev build](https://github.com/anthonyaxenov/atol-online/actions/workflows/dev.yml/badge.svg)](https://github.com/anthonyaxenov/atol-online/actions/workflows/dev.yml) -[![Latest Stable Version](http://poser.pugx.org/axenov/atol-online/v)](https://packagist.org/packages/axenov/atol-online) -[![codecov](https://codecov.io/gh/anthonyaxenov/atol-online/branch/master/graph/badge.svg?token=WR2IV7FTF0)](https://codecov.io/gh/anthonyaxenov/atol-online) +[![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) +[![Total Downloads](http://poser.pugx.org/axenov/atol-online/downloads)](https://packagist.org/packages/axenov/atol-online) [![License](http://poser.pugx.org/axenov/atol-online/license)](https://packagist.org/packages/axenov/atol-online) - - Библиотека для фискализации чеков по 54-ФЗ через [облачную ККТ АТОЛ](https://online.atol.ru/). **[Документация](/docs/readme.md)** +--- + +**В ветке `dev` проводится глубокий рефакторинг, стабилизация и активная подготовка к `v1.0.0`. +Документация актуализируется постепенно.** + +--- + +| master | [![CI](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=master)](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 | [![CI dev](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=dev)](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) | + Текущие поддерживаемые версии АТОЛ Онлайн: | Протокол | API | ФФД | Статус | @@ -32,8 +33,8 @@ * Фискализация докумнетов на облачной ККТ * Валидация данных до отправки документа на ККТ (насколько это возможно, согласно схеме) * Расчёты денег в копейках -* Фактически полное покрытие тестами * PSR-4 автозагрузка + ## Системные требования From 2a66889e46d92d59768de3b41b07cf55b7742a46 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 11:52:40 +0800 Subject: [PATCH 44/85] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20`nomenclature=5Fcode`=20=D1=83=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=BC=D0=B5=D1=82=D0=B0=20=D1=80=D0=B0=D1=81?= =?UTF-8?q?=D1=87=D1=91=D1=82=D0=B0=20+=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=BE?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - теперь `getSum()` проверяет по `Constraints::MAX_COUNT_ITEM_SUM` вместо `MAX_COUNT_ITEM_PRICE` (как и должен был изначально) - подправил `TooLongException` - всякие phpdoc-и --- src/Constants/Constraints.php | 10 ++- src/Entities/Item.php | 58 +++++++++++- src/Exceptions/TooLongException.php | 13 ++- src/Exceptions/TooLongItemCodeException.php | 35 ++++++++ tests/AtolOnline/Tests/Entities/ItemTest.php | 95 ++++++++++++++++++-- 5 files changed, 192 insertions(+), 19 deletions(-) create mode 100644 src/Exceptions/TooLongItemCodeException.php diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index e3c1350..aa4bc0e 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -105,7 +105,7 @@ final class Constraints /** * Минимальная длина кода таможенной декларации (1231) * - * @see https://online.atol.ru/possystem/v4/schema/sell Схема receipt.items.declaration_number + * @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/items/declaration_number" */ const MIN_LENGTH_DECLARATION_NUMBER = 1; @@ -135,6 +135,13 @@ final class Constraints */ 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; + /** * Регулярное выражение для валидации строки ИНН * @@ -145,7 +152,6 @@ final class Constraints /** * Регулярное выражение для валидации номера телефона - * * @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/definitions/phone_number" */ const PATTERN_PHONE = /* @lang PhpRegExp */ diff --git a/src/Entities/Item.php b/src/Entities/Item.php index aba875d..9cb0fc1 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -26,6 +26,7 @@ use AtolOnline\{ Exceptions\TooHighItemQuantityException, Exceptions\TooHighPriceException, Exceptions\TooHighSumException, + Exceptions\TooLongItemCodeException, Exceptions\TooLongItemNameException, Exceptions\TooLongMeasurementUnitException, Exceptions\TooLongUserdataException, @@ -59,6 +60,16 @@ class Item extends Entity */ 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) */ @@ -229,7 +240,7 @@ class Item extends Entity public function getSum(): float { $sum = $this->getPrice() * $this->getQuantity() + (float)$this->getExcise(); - if ($sum > Constraints::MAX_COUNT_ITEM_PRICE) { + if ($sum > Constraints::MAX_COUNT_ITEM_SUM) { throw new TooHighSumException($this->getName(), $sum); } return $sum; @@ -262,6 +273,49 @@ class Item extends Entity 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; + } + /** * Возвращает признак способа оплаты * @@ -536,6 +590,7 @@ class Item extends Entity '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(); @@ -545,7 +600,6 @@ class Item extends Entity !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(); - //TODO nomenclature_code return $json; } } diff --git a/src/Exceptions/TooLongException.php b/src/Exceptions/TooLongException.php index d26748c..952af10 100644 --- a/src/Exceptions/TooLongException.php +++ b/src/Exceptions/TooLongException.php @@ -31,14 +31,13 @@ class TooLongException extends AtolException * * @param string $value * @param string $message - * @param int $max + * @param float $max */ - public function __construct(string $value, string $message = '', int $max = 0) + public function __construct(string $value, string $message = '', float $max = 0) { - $message = ($message ?: $this->message) . ': '. $value; - if ($max > 0 || $this->max > 0) { - $message .= ' (макс. = ' . ($max ?? $this->max) . ', фактически = ' . mb_strlen($value) . ')'; - } - parent::__construct($message); + parent::__construct( + ($message ?: $this->message) . ': ' . $value . (((float)$max > 0 || (float)$this->max > 0) ? + ' (макс = ' . ($max ?: $this->max) . ', фактически = ' . mb_strlen($value) . ')' : '') + ); } } diff --git a/src/Exceptions/TooLongItemCodeException.php b/src/Exceptions/TooLongItemCodeException.php new file mode 100644 index 0000000..e9dba47 --- /dev/null +++ b/src/Exceptions/TooLongItemCodeException.php @@ -0,0 +1,35 @@ +expectException(NegativeItemExciseException::class); (new Item('test item', 2, 3))->setExcise(-1); } + + /** + * Тестирует установку валидного кода товара + * + * @covers \AtolOnline\Entities\Item::setCode + * @covers \AtolOnline\Entities\Item::getCode + * @covers \AtolOnline\Entities\Item::getCodeHex + * @covers \AtolOnline\Entities\Item::jsonSerialize + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws TooLongItemCodeException + */ + public function testValidNomenclatureCode(): void + { + $code = Helpers::randomStr(Constraints::MAX_LENGTH_ITEM_CODE); + $encoded = trim(preg_replace('/([\dA-Fa-f]{2})/', '$1 ', bin2hex($code))); + + $item = (new Item('test item', 2, 3))->setCode($code); + $this->assertEquals($code, $item->getCode()); + $this->assertEquals($encoded, $item->getCodeHex()); + + $decoded = hex2bin(str_replace(' ', '', $item->getCodeHex())); + $this->assertEquals($decoded, $item->getCode()); + + $this->assertAtolable($item, [ + 'name' => 'test item', + 'price' => 2, + 'quantity' => 3, + 'sum' => 6, + 'nomenclature_code' => $item->getCodeHex(), + ]); + } + + /** + * Тестирует обнуление кода товара + * + * @param mixed $param + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Item::setCode + * @covers \AtolOnline\Entities\Item::getCode + * @covers \AtolOnline\Entities\Item::getCodeHex + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighPriceException + * @throws TooLongItemCodeException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testNullableCode(mixed $param): void + { + $item = (new Item('test item', 2, 3))->setCode($param); + $this->assertNull($item->getCode()); + $this->assertNull($item->getCodeHex()); + } + + /** + * Тестирует выброс исключения при установке слишком отрицательного акциза + * + * @covers \AtolOnline\Entities\Item::setCode + * @covers \AtolOnline\Exceptions\TooLongItemCodeException + * @throws TooLongItemNameException + * @throws TooHighPriceException + * @throws TooManyException + * @throws NegativeItemPriceException + * @throws EmptyItemNameException + * @throws NegativeItemQuantityException + */ + public function testTooLongItemCodeException(): void + { + $this->expectException(TooLongItemCodeException::class); + (new Item('test item', 2, 3))->setCode(Helpers::randomStr(Constraints::MAX_LENGTH_ITEM_CODE + 1)); + } } From 85750cd211b8724c0b745e19e8e3820c880ec814 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 18:10:06 +0800 Subject: [PATCH 45/85] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Также подключен jetbrains/phpstorm-attributes, чтобы всё по красоте --- composer.json | 3 +- composer.lock | 300 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 263 insertions(+), 40 deletions(-) diff --git a/composer.json b/composer.json index c78cdf8..3e9a9a1 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,8 @@ "psr/log": "^3", "ramsey/uuid": "^4.2", "myclabs/php-enum": "^1.8", - "illuminate/collections": "^8.70" + "illuminate/collections": "^8.70", + "jetbrains/phpstorm-attributes": "^1.0" }, "require-dev": { "phpunit/phpunit": "^9.5" diff --git a/composer.lock b/composer.lock index 8acd671..95f535a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b03e06fba2a861f7ce366484ec6421c3", + "content-hash": "4877f27cd59b6558eea01644e2d520ef", "packages": [ { "name": "brick/math", @@ -50,6 +50,10 @@ "brick", "math" ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.9.3" + }, "funding": [ { "url": "https://github.com/BenMorel", @@ -166,6 +170,10 @@ "rest", "web service" ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -246,6 +254,10 @@ "keywords": [ "promise" ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -357,6 +369,10 @@ "uri", "url" ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.1.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -375,16 +391,16 @@ }, { "name": "illuminate/collections", - "version": "v8.70.2", + "version": "v8.74.0", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "05f286ec5fd2dd286e8384577047efc375c8954c" + "reference": "f292b77824b42cd28decc7327e7c2e24c3806371" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/05f286ec5fd2dd286e8384577047efc375c8954c", - "reference": "05f286ec5fd2dd286e8384577047efc375c8954c", + "url": "https://api.github.com/repos/illuminate/collections/zipball/f292b77824b42cd28decc7327e7c2e24c3806371", + "reference": "f292b77824b42cd28decc7327e7c2e24c3806371", "shasum": "" }, "require": { @@ -393,7 +409,7 @@ "php": "^7.3|^8.0" }, "suggest": { - "symfony/var-dumper": "Required to use the dump method (^5.1.4)." + "symfony/var-dumper": "Required to use the dump method (^5.4)." }, "type": "library", "extra": { @@ -421,20 +437,24 @@ ], "description": "The Illuminate Collections package.", "homepage": "https://laravel.com", - "time": "2021-10-22T18:01:46+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-30T14:29:03+00:00" }, { "name": "illuminate/contracts", - "version": "v8.70.2", + "version": "v8.74.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "e76f4bce73a2a1656add24bd5210ebc4b8af49c0" + "reference": "b0886ec05a63b204634d64d0b39d5b78a7c06f81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/e76f4bce73a2a1656add24bd5210ebc4b8af49c0", - "reference": "e76f4bce73a2a1656add24bd5210ebc4b8af49c0", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/b0886ec05a63b204634d64d0b39d5b78a7c06f81", + "reference": "b0886ec05a63b204634d64d0b39d5b78a7c06f81", "shasum": "" }, "require": { @@ -465,20 +485,24 @@ ], "description": "The Illuminate Contracts package.", "homepage": "https://laravel.com", - "time": "2021-10-22T18:01:46+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-17T15:04:30+00:00" }, { "name": "illuminate/macroable", - "version": "v8.70.2", + "version": "v8.74.0", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", - "reference": "300aa13c086f25116b5f3cde3ca54ff5c822fb05" + "reference": "aed81891a6e046fdee72edd497f822190f61c162" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/300aa13c086f25116b5f3cde3ca54ff5c822fb05", - "reference": "300aa13c086f25116b5f3cde3ca54ff5c822fb05", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/aed81891a6e046fdee72edd497f822190f61c162", + "reference": "aed81891a6e046fdee72edd497f822190f61c162", "shasum": "" }, "require": { @@ -507,7 +531,53 @@ ], "description": "The Illuminate Macroable package.", "homepage": "https://laravel.com", - "time": "2020-10-27T15:20:30+00:00" + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-16T13:57:03+00:00" + }, + { + "name": "jetbrains/phpstorm-attributes", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/JetBrains/phpstorm-attributes.git", + "reference": "a7a83ae5df4dd3c0875484483de19de8edf60a9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JetBrains/phpstorm-attributes/zipball/a7a83ae5df4dd3c0875484483de19de8edf60a9f", + "reference": "a7a83ae5df4dd3c0875484483de19de8edf60a9f", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "JetBrains\\PhpStorm\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "JetBrains", + "homepage": "https://www.jetbrains.com" + } + ], + "description": "PhpStorm specific attributes", + "keywords": [ + "attributes", + "jetbrains", + "phpstorm" + ], + "support": { + "issues": "https://youtrack.jetbrains.com/newIssue?project=WI", + "source": "https://github.com/JetBrains/phpstorm-attributes/tree/1.0" + }, + "time": "2020-11-17T11:09:47+00:00" }, { "name": "myclabs/php-enum", @@ -553,6 +623,10 @@ "keywords": [ "enum" ], + "support": { + "issues": "https://github.com/myclabs/php-enum/issues", + "source": "https://github.com/myclabs/php-enum/tree/1.8.3" + }, "funding": [ { "url": "https://github.com/mnapoli", @@ -607,6 +681,10 @@ "container-interop", "psr" ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, "time": "2021-11-05T16:50:12+00:00" }, { @@ -656,6 +734,9 @@ "psr", "psr-18" ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, "time": "2020-06-29T06:28:15+00:00" }, { @@ -708,6 +789,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, "time": "2019-04-30T12:38:16+00:00" }, { @@ -808,6 +892,9 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, "time": "2021-07-14T16:46:02+00:00" }, { @@ -856,6 +943,9 @@ "psr-16", "simple-cache" ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, "time": "2017-10-23T01:57:42+00:00" }, { @@ -965,6 +1055,10 @@ "queue", "set" ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/1.2.2" + }, "funding": [ { "url": "https://github.com/ramsey", @@ -1059,6 +1153,10 @@ "identifier", "uuid" ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.2.3" + }, "funding": [ { "url": "https://github.com/ramsey", @@ -1073,16 +1171,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", "shasum": "" }, "require": { @@ -1091,7 +1189,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1119,6 +1217,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1133,7 +1234,7 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1195,6 +1296,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1275,6 +1379,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1351,6 +1458,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1498,16 +1608,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.13.1", + "version": "v4.13.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", - "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", "shasum": "" }, "require": { @@ -1546,7 +1656,11 @@ "parser", "php" ], - "time": "2021-11-03T20:52:16+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + }, + "time": "2021-11-30T19:35:32+00:00" }, { "name": "phar-io/manifest", @@ -1602,6 +1716,10 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, "time": "2021-07-20T11:28:43+00:00" }, { @@ -1759,6 +1877,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, "time": "2021-10-19T17:43:47+00:00" }, { @@ -1805,6 +1927,10 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" + }, "time": "2021-10-02T14:08:47+00:00" }, { @@ -1868,20 +1994,24 @@ "spy", "stub" ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + }, "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.8", + "version": "9.2.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e" + "reference": "f301eb1453c9e7a1bc912ee8b0ea9db22c60223b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", - "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f301eb1453c9e7a1bc912ee8b0ea9db22c60223b", + "reference": "f301eb1453c9e7a1bc912ee8b0ea9db22c60223b", "shasum": "" }, "require": { @@ -1935,26 +2065,30 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.9" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2021-10-30T08:01:38+00:00" + "time": "2021-11-19T15:21:02+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -1991,13 +2125,17 @@ "filesystem", "iterator" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-09-28T05:57:25+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -2050,6 +2188,10 @@ "keywords": [ "process" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2105,6 +2247,10 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2160,6 +2306,10 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2255,6 +2405,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -2311,6 +2465,10 @@ ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2363,6 +2521,10 @@ ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2414,6 +2576,10 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2484,6 +2650,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2537,6 +2707,10 @@ ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2599,6 +2773,10 @@ "unidiff", "unified diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2658,6 +2836,10 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2731,6 +2913,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2791,6 +2977,10 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2844,6 +3034,10 @@ ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2897,6 +3091,10 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2948,6 +3146,10 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3007,6 +3209,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3058,6 +3264,10 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3110,6 +3320,10 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3159,6 +3373,10 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3205,6 +3423,10 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, "funding": [ { "url": "https://github.com/theseer", @@ -3283,5 +3505,5 @@ "ext-mbstring": "*" }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.1.0" } From 8f235d47303d92a8e944db1d9e1580f49be77ac1 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 18:15:16 +0800 Subject: [PATCH 46/85] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D1=91=D0=BD=20RO?= =?UTF-8?q?ADMAP,=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++--- ROADMAP.md | 66 ------------------------------------------------------ 2 files changed, 3 insertions(+), 70 deletions(-) delete mode 100644 ROADMAP.md diff --git a/README.md b/README.md index 55de7a0..a600739 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ --- -| master | [![CI](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=master)](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 | [![CI dev](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=dev)](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) | +| master | [![CI](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=master)](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 | [![CI dev](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=dev)](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) | Текущие поддерживаемые версии АТОЛ Онлайн: @@ -92,7 +92,6 @@ composer test-cov # тестирование с покрытием * **[Документация к библиотеке](/docs/readme.md)** * Telegram-канал: [@atolonline_php](https://t.me/atolonline_php) * [Документация АТОЛ Онлайн](https://online.atol.ru/lib/) -* Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md) ## Лицензия diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index e63c986..0000000 --- a/ROADMAP.md +++ /dev/null @@ -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` From c30c7d069f2f5e5f6791e4c57517fb46e95e9f7a Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 18:16:28 +0800 Subject: [PATCH 47/85] =?UTF-8?q?=D0=9D=D0=B5=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D1=8B=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BD=D0=B0=D0=B3=D0=BB=D1=8F=D0=B4=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ectionInfo.php => todo_CorrectionInfo.php} | 12 ++--- .../{Document.php => todo_Document.php} | 50 +++++++++---------- .../{Payment.php => todo_Payment.php} | 26 +++++----- 3 files changed, 44 insertions(+), 44 deletions(-) rename src/Entities/{CorrectionInfo.php => todo_CorrectionInfo.php} (89%) rename src/Entities/{Document.php => todo_Document.php} (90%) rename src/Entities/{Payment.php => todo_Payment.php} (86%) diff --git a/src/Entities/CorrectionInfo.php b/src/Entities/todo_CorrectionInfo.php similarity index 89% rename from src/Entities/CorrectionInfo.php rename to src/Entities/todo_CorrectionInfo.php index 6a06f82..f404f17 100644 --- a/src/Entities/CorrectionInfo.php +++ b/src/Entities/todo_CorrectionInfo.php @@ -12,9 +12,9 @@ declare(strict_types = 1); namespace AtolOnline\Entities; /** - * Класс CorrectionInfo, описывающий данные чек коррекции + * Класс todoCorrectionInfo, описывающий данные чек коррекции */ -class CorrectionInfo extends Entity +class todoCorrectionInfo extends Entity { /** * @var string Тип коррекции. Тег ФФД - 1173. @@ -32,7 +32,7 @@ class CorrectionInfo extends Entity protected string $base_number; /** - * CorrectionInfo constructor. + * todoCorrectionInfo constructor. * * @param string|null $type Тип коррекции * @param string|null $base_date Дата документа @@ -66,7 +66,7 @@ class CorrectionInfo extends Entity * @param string $number * @return $this */ - public function setNumber(string $number): CorrectionInfo + public function setNumber(string $number): todoCorrectionInfo { $this->base_number = trim($number); return $this; @@ -90,7 +90,7 @@ class CorrectionInfo extends Entity * @param string $date Строка в формате d.m.Y * @return $this */ - public function setDate(string $date): CorrectionInfo + public function setDate(string $date): todoCorrectionInfo { $this->base_date = $date; return $this; @@ -114,7 +114,7 @@ class CorrectionInfo extends Entity * @param string $type * @return $this */ - public function setType(string $type): CorrectionInfo + public function setType(string $type): todoCorrectionInfo { $this->type = $type; return $this; diff --git a/src/Entities/Document.php b/src/Entities/todo_Document.php similarity index 90% rename from src/Entities/Document.php rename to src/Entities/todo_Document.php index 8c88432..f79bde2 100644 --- a/src/Entities/Document.php +++ b/src/Entities/todo_Document.php @@ -35,7 +35,7 @@ use Exception; * * @package AtolOnline\Entities */ -class Document extends Entity +class todoDocument extends Entity { /** * @var ItemArray Массив предметов расчёта @@ -73,12 +73,12 @@ class Document extends Entity protected string $cashier; /** - * @var CorrectionInfo Данные коррекции + * @var todoCorrectionInfo Данные коррекции */ - protected CorrectionInfo $correction_info; + protected todoCorrectionInfo $correction_info; /** - * Document constructor. + * todoDocument constructor. */ public function __construct() { @@ -93,7 +93,7 @@ class Document extends Entity * @return $this * @throws TooManyVatsException Слишком много ставок НДС */ - public function clearVats(): Document + public function clearVats(): todoDocument { $this->setVats([]); return $this; @@ -106,7 +106,7 @@ class Document extends Entity * @return $this * @throws TooManyVatsException Слишком много ставок НДС */ - public function addVat(Vat $vat): Document + public function addVat(Vat $vat): todoDocument { $this->vats->add($vat); return $this; @@ -130,7 +130,7 @@ class Document extends Entity * @throws TooManyVatsException Слишком много ставок НДС * @throws Exception */ - public function setVats(array $vats): Document + public function setVats(array $vats): todoDocument { $this->vats->set($vats); return $this; @@ -139,12 +139,12 @@ class Document extends Entity /** * Добавляет новую оплату в массив оплат * - * @param Payment $payment Объект оплаты + * @param todoPayment $payment Объект оплаты * @return $this * @throws Exception * @throws TooManyPaymentsException Слишком много оплат */ - public function addPayment(Payment $payment): Document + public function addPayment(todoPayment $payment): todoDocument { if (count($this->getPayments()) == 0 && !$payment->getSum()) { $payment->setSum($this->calcTotal()); @@ -156,7 +156,7 @@ class Document extends Entity /** * Возвращает массив оплат * - * @return Payment[] + * @return todoPayment[] */ public function getPayments(): array { @@ -166,11 +166,11 @@ class Document extends Entity /** * Устанавливает массив оплат * - * @param Payment[] $payments Массив оплат + * @param todoPayment[] $payments Массив оплат * @return $this * @throws TooManyPaymentsException Слишком много оплат */ - public function setPayments(array $payments): Document + public function setPayments(array $payments): todoDocument { $this->payments->set($payments); return $this; @@ -183,7 +183,7 @@ class Document extends Entity * @return $this * @throws TooManyItemsException Слишком много предметов расчёта */ - public function addItem(Item $item): Document + public function addItem(Item $item): todoDocument { $this->items->add($item); return $this; @@ -206,7 +206,7 @@ class Document extends Entity * @return $this * @throws TooManyItemsException Слишком много предметов расчёта */ - public function setItems(array $items): Document + public function setItems(array $items): todoDocument { $this->items->set($items); return $this; @@ -228,7 +228,7 @@ class Document extends Entity * @param Client|null $client * @return $this */ - public function setClient(?Client $client): Document + public function setClient(?Client $client): todoDocument { $this->client = $client; return $this; @@ -250,7 +250,7 @@ class Document extends Entity * @param Company|null $company * @return $this */ - public function setCompany(?Company $company): Document + public function setCompany(?Company $company): todoDocument { $this->company = $company; return $this; @@ -273,7 +273,7 @@ class Document extends Entity * @return $this * @throws TooLongCashierException */ - public function setCashier(?string $cashier): Document + public function setCashier(?string $cashier): todoDocument { if ($cashier !== null) { $cashier = trim($cashier); @@ -288,9 +288,9 @@ class Document extends Entity /** * Возвращает данные коррекции * - * @return CorrectionInfo|null + * @return todoCorrectionInfo|null */ - public function getCorrectionInfo(): ?CorrectionInfo + public function getCorrectionInfo(): ?todoCorrectionInfo { return $this->correction_info; } @@ -298,10 +298,10 @@ class Document extends Entity /** * Устанавливает данные коррекции * - * @param CorrectionInfo|null $correction_info + * @param todoCorrectionInfo|null $correction_info * @return $this */ - public function setCorrectionInfo(?CorrectionInfo $correction_info): Document + public function setCorrectionInfo(?todoCorrectionInfo $correction_info): todoDocument { $this->correction_info = $correction_info; return $this; @@ -338,7 +338,7 @@ class Document extends Entity * Собирает объект документа из сырой json-строки * * @param string $json - * @return Document + * @return todoDocument * @throws TooLongEmailException * @throws InvalidEmailException * @throws AtolException @@ -355,7 +355,7 @@ class Document extends Entity * @throws TooLongUserdataException * @throws Exception */ - public static function fromRaw(string $json): Document + public static function fromRaw(string $json): todoDocument { $array = json_decode($json, true); if (json_last_error() !== JSON_ERROR_NONE) { @@ -379,7 +379,7 @@ class Document extends Entity )); } if (isset($array['correction_info'])) { - $doc->setCorrectionInfo(new CorrectionInfo( + $doc->setCorrectionInfo(new todoCorrectionInfo( $array['correction_info']['type'] ?? null, $array['correction_info']['base_date'] ?? null, $array['correction_info']['base_number'] ?? null, @@ -405,7 +405,7 @@ class Document extends Entity } if (isset($array['payments'])) { foreach ($array['payments'] as $ar_payment) { - $payment = new Payment(); + $payment = new todoPayment(); if (isset($ar_payment['type'])) { $payment->setType($ar_payment['type']); } diff --git a/src/Entities/Payment.php b/src/Entities/todo_Payment.php similarity index 86% rename from src/Entities/Payment.php rename to src/Entities/todo_Payment.php index bc8fd99..6398a42 100644 --- a/src/Entities/Payment.php +++ b/src/Entities/todo_Payment.php @@ -18,30 +18,30 @@ use AtolOnline\Enums\PaymentTypes; * * @package AtolOnline\Entities */ -class Payment extends Entity +class todoPayment extends Entity { /** * @var int Тип оплаты */ protected int $type; - + /** * @var float Сумма оплаты */ protected float $sum; - + /** - * Payment constructor. + * todoPayment constructor. * - * @param int $payment_type Тип оплаты - * @param float $sum Сумма оплаты + * @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. * @@ -51,19 +51,19 @@ class Payment extends Entity { return $this->type; } - + /** * Устанавливает тип оплаты. Тег ФФД - 1031, 1081, 1215, 1216, 1217. * * @param int $type * @return $this */ - public function setType(int $type): Payment + public function setType(int $type): todoPayment { $this->type = $type; return $this; } - + /** * Возвращает сумму оплаты * @@ -73,19 +73,19 @@ class Payment extends Entity { return $this->sum; } - + /** * Устанавливает сумму оплаты * * @param float $sum * @return $this */ - public function setSum(float $sum): Payment + public function setSum(float $sum): todoPayment { $this->sum = $sum; return $this; } - + /** * @inheritDoc */ From 2260233e3ffdf2fef65d1ad247b453d462c4a5b0 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 18:23:00 +0800 Subject: [PATCH 48/85] =?UTF-8?q?=D0=9E=D0=B1=D1=89=D0=B8=D0=B5=20=D1=81?= =?UTF-8?q?=D0=B5=D1=82=D1=82=D0=B5=D1=80=D1=8B-=D0=B3=D0=B5=D1=82=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D1=8B=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D0=B9=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B?= =?UTF-8?q?=20=D0=B2=20=D1=82=D1=80=D0=B5=D0=B9=D1=82=D1=8B=20`HasEmail`,?= =?UTF-8?q?=20`HasInn`,=20`HasPhones`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кодстайл и микрорефакторинг сущностей --- src/Entities/AgentInfo.php | 38 ++++----- src/Entities/Client.php | 103 +++++------------------ src/Entities/Company.php | 91 +++++--------------- src/Entities/Item.php | 70 ++++++--------- src/Entities/Kkt.php | 12 +-- src/Entities/MoneyTransferOperator.php | 76 +++-------------- src/Entities/PayingAgent.php | 48 ++--------- src/Entities/ReceivePaymentsOperator.php | 46 +--------- src/Entities/Supplier.php | 86 +++---------------- src/Entities/Vat.php | 21 +++-- src/Traits/HasEmail.php | 57 +++++++++++++ src/Traits/HasInn.php | 53 ++++++++++++ src/Traits/HasPhones.php | 57 +++++++++++++ 13 files changed, 305 insertions(+), 453 deletions(-) create mode 100644 src/Traits/HasEmail.php create mode 100644 src/Traits/HasInn.php create mode 100644 src/Traits/HasPhones.php diff --git a/src/Entities/AgentInfo.php b/src/Entities/AgentInfo.php index 346cd4a..6b4ddec 100644 --- a/src/Entities/AgentInfo.php +++ b/src/Entities/AgentInfo.php @@ -45,21 +45,21 @@ class AgentInfo extends Entity * Конструктор * * @param string|null $type Признак агента (1057) - * @param PayingAgent|null $paying_agent Платёжный агент - * @param ReceivePaymentsOperator|null $receive_payments_operator Оператор по приёму платежей - * @param MoneyTransferOperator|null $money_transfer_operator Оператор перевода + * @param PayingAgent|null $pagent Платёжный агент + * @param ReceivePaymentsOperator|null $rp_operator Оператор по приёму платежей + * @param MoneyTransferOperator|null $mt_operator Оператор перевода * @throws InvalidEnumValueException */ public function __construct( ?string $type = null, - ?PayingAgent $paying_agent = null, - ?ReceivePaymentsOperator $receive_payments_operator = null, - ?MoneyTransferOperator $money_transfer_operator = null, + ?PayingAgent $pagent = null, + ?ReceivePaymentsOperator $rp_operator = null, + ?MoneyTransferOperator $mt_operator = null, ) { !is_null($type) && $this->setType($type); - !is_null($paying_agent) && $this->setPayingAgent($paying_agent); - !is_null($receive_payments_operator) && $this->setReceivePaymentsOperator($receive_payments_operator); - !is_null($money_transfer_operator) && $this->setMoneyTransferOperator($money_transfer_operator); + !is_null($pagent) && $this->setPayingAgent($pagent); + !is_null($rp_operator) && $this->setReceivePaymentsOperator($rp_operator); + !is_null($mt_operator) && $this->setMoneyTransferOperator($mt_operator); } /** @@ -79,7 +79,7 @@ class AgentInfo extends Entity * @return AgentInfo * @throws InvalidEnumValueException */ - public function setType(?string $type): AgentInfo + public function setType(?string $type): self { AgentTypes::isValid($type) && $this->type = $type; return $this; @@ -98,12 +98,12 @@ class AgentInfo extends Entity /** * Устанавливает платёжного агента * - * @param PayingAgent|null $paying_agent + * @param PayingAgent|null $agent * @return AgentInfo */ - public function setPayingAgent(?PayingAgent $paying_agent): AgentInfo + public function setPayingAgent(?PayingAgent $agent): self { - $this->paying_agent = $paying_agent; + $this->paying_agent = $agent; return $this; } @@ -120,12 +120,12 @@ class AgentInfo extends Entity /** * Устанавливает оператора по приёму платежей * - * @param ReceivePaymentsOperator|null $receive_payments_operator + * @param ReceivePaymentsOperator|null $operator * @return AgentInfo */ - public function setReceivePaymentsOperator(?ReceivePaymentsOperator $receive_payments_operator): AgentInfo + public function setReceivePaymentsOperator(?ReceivePaymentsOperator $operator): self { - $this->receive_payments_operator = $receive_payments_operator; + $this->receive_payments_operator = $operator; return $this; } @@ -142,12 +142,12 @@ class AgentInfo extends Entity /** * Устанавливает оператора перевода * - * @param MoneyTransferOperator|null $money_transfer_operator + * @param MoneyTransferOperator|null $operator * @return AgentInfo */ - public function setMoneyTransferOperator(?MoneyTransferOperator $money_transfer_operator): AgentInfo + public function setMoneyTransferOperator(?MoneyTransferOperator $operator): self { - $this->money_transfer_operator = $money_transfer_operator; + $this->money_transfer_operator = $operator; return $this; } diff --git a/src/Entities/Client.php b/src/Entities/Client.php index e0672c1..4e54f37 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -11,14 +11,19 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\{ - Constants\Constraints, - Exceptions\InvalidEmailException, - Exceptions\InvalidInnLengthException, - Exceptions\InvalidPhoneException, - Exceptions\TooLongClientContactException, - Exceptions\TooLongClientNameException, - Exceptions\TooLongEmailException}; +use AtolOnline\Constants\Constraints; +use AtolOnline\Exceptions\{ + InvalidEmailException, + InvalidInnLengthException, + InvalidPhoneException, + TooLongClientNameException, + TooLongEmailException +}; +use AtolOnline\Traits\{ + HasEmail, + HasInn +}; +use JetBrains\PhpStorm\Pure; /** * Класс, описывающий покупателя @@ -27,38 +32,30 @@ use AtolOnline\{ */ class Client extends Entity { + use HasEmail, HasInn; + /** * @var string|null Наименование (1227) */ protected ?string $name = null; - /** - * @var string|null Email (1008) - */ - protected ?string $email = null; - /** * @var string|null Телефон (1008) */ protected ?string $phone = null; - /** - * @var string|null ИНН (1228) - */ - protected ?string $inn = null; - /** * Конструктор объекта покупателя * * @param string|null $name Наименование (1227) - * @param string|null $phone Email (1008) * @param string|null $email Телефон (1008) + * @param string|null $phone Email (1008) * @param string|null $inn ИНН (1228) - * @throws TooLongClientNameException - * @throws TooLongClientContactException - * @throws TooLongEmailException * @throws InvalidEmailException * @throws InvalidInnLengthException + * @throws InvalidPhoneException + * @throws TooLongClientNameException + * @throws TooLongEmailException */ public function __construct( ?string $name = null, @@ -101,38 +98,6 @@ class Client extends Entity return $this; } - /** - * Возвращает установленный email - * - * @return string|null - */ - public function getEmail(): ?string - { - return $this->email; - } - - /** - * Устанавливает email - * - * @param string|null $email - * @return $this - * @throws TooLongEmailException Слишком длинный email - * @throws InvalidEmailException Невалидный email - */ - public function setEmail(?string $email): self - { - if (is_string($email)) { - $email = preg_replace('/[\n\r\t]/', '', trim($email)); - if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) { - throw new TooLongEmailException($email); - } elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { - throw new InvalidEmailException($email); - } - } - $this->email = empty($email) ? null : $email; - return $this; - } - /** * Возвращает установленный телефон * @@ -162,38 +127,10 @@ class Client extends Entity return $this; } - /** - * Возвращает установленный ИНН - * - * @return string|null - */ - public function getInn(): ?string - { - return $this->inn; - } - - /** - * Устанавливает ИНН - * - * @param string|null $inn - * @return $this - * @throws InvalidInnLengthException Некорректная длина ИНН - */ - public function setInn(?string $inn): self - { - if (is_string($inn)) { - $inn = preg_replace('/[^\d]/', '', trim($inn)); - if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { - throw new InvalidInnLengthException($inn); - } - } - $this->inn = empty($inn) ? null : $inn; - return $this; - } - /** * @inheritDoc */ + #[Pure] public function jsonSerialize(): array { $json = []; diff --git a/src/Entities/Company.php b/src/Entities/Company.php index 73868d7..aeb556e 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -14,12 +14,18 @@ namespace AtolOnline\Entities; use AtolOnline\{ Constants\Constraints, Enums\SnoTypes, - Exceptions\InvalidEmailException, - Exceptions\InvalidEnumValueException, - Exceptions\InvalidInnLengthException, - Exceptions\InvalidPaymentAddressException, - Exceptions\TooLongEmailException, - Exceptions\TooLongPaymentAddressException}; + Traits\HasEmail, + Traits\HasInn +}; +use AtolOnline\Exceptions\{ + InvalidEmailException, + InvalidEnumValueException, + InvalidInnLengthException, + InvalidPaymentAddressException, + TooLongEmailException, + TooLongPaymentAddressException +}; +use JetBrains\PhpStorm\ArrayShape; /** * Класс, описывающий сущность компании-продавца @@ -28,21 +34,13 @@ use AtolOnline\{ */ class Company extends Entity { - /** - * @var string|null Почта (1117) - */ - protected ?string $email; + use HasEmail, HasInn; /** * @var string|null Система налогообложения продавца (1055) */ protected ?string $sno; - /** - * @var string|null ИНН (1018) - */ - protected ?string $inn; - /** * @var string|null Место расчётов (адрес интернет-магазина) (1187) */ @@ -74,36 +72,6 @@ class Company extends Entity $this->setPaymentAddress($payment_address); } - /** - * Возвращает установленный email - * - * @return string - */ - public function getEmail(): string - { - return $this->email; - } - - /** - * Устанавливает email - * - * @param string $email - * @return $this - * @throws TooLongEmailException Слишком длинный email - * @throws InvalidEmailException Невалидный email - */ - public function setEmail(string $email): self - { - $email = preg_replace('/[\n\r\t]/', '', trim($email)); - if (mb_strlen($email) > Constraints::MAX_LENGTH_EMAIL) { - throw new TooLongEmailException($email); - } elseif (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) { - throw new InvalidEmailException($email); - } - $this->email = $email; - return $this; - } - /** * Возвращает установленный тип налогообложения * @@ -129,33 +97,6 @@ class Company extends Entity return $this; } - /** - * Возвращает установленный ИНН - * - * @return string - */ - public function getInn(): string - { - return $this->inn; - } - - /** - * Устанавливает ИНН - * - * @param string $inn - * @return $this - * @throws InvalidInnLengthException - */ - public function setInn(string $inn): self - { - $inn = preg_replace('/[^\d]/', '', trim($inn)); - if (empty($inn) || preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { - throw new InvalidInnLengthException($inn); - } - $this->inn = $inn; - return $this; - } - /** * Возвращает установленный адрес места расчётов * @@ -193,6 +134,12 @@ class Company extends Entity * @throws InvalidInnLengthException * @throws InvalidPaymentAddressException */ + #[ArrayShape([ + 'email' => "string", + 'sno' => "string", + 'inn' => "string", + 'payment_address' => "string", + ])] public function jsonSerialize(): array { return [ diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 9cb0fc1..7fcc45e 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -11,26 +11,28 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\{ - Constants\Constraints, - Enums\PaymentMethods, - Enums\PaymentObjects, - Enums\VatTypes, - Exceptions\EmptyItemNameException, - Exceptions\InvalidDeclarationNumberException, - Exceptions\InvalidEnumValueException, - Exceptions\InvalidOKSMCodeException, - Exceptions\NegativeItemExciseException, - Exceptions\NegativeItemPriceException, - Exceptions\NegativeItemQuantityException, - Exceptions\TooHighItemQuantityException, - Exceptions\TooHighPriceException, - Exceptions\TooHighSumException, - Exceptions\TooLongItemCodeException, - Exceptions\TooLongItemNameException, - Exceptions\TooLongMeasurementUnitException, - Exceptions\TooLongUserdataException, - Exceptions\TooManyException +use AtolOnline\Constants\Constraints; +use AtolOnline\Enums\{ + PaymentMethods, + PaymentObjects, + VatTypes +}; +use AtolOnline\Exceptions\{ + EmptyItemNameException, + InvalidDeclarationNumberException, + InvalidEnumValueException, + InvalidOKSMCodeException, + NegativeItemExciseException, + NegativeItemPriceException, + NegativeItemQuantityException, + TooHighItemQuantityException, + TooHighPriceException, + TooHighSumException, + TooLongItemCodeException, + TooLongItemNameException, + TooLongMeasurementUnitException, + TooLongUserdataException, + TooManyException }; /** @@ -153,7 +155,7 @@ class Item extends Entity * * @param string $name Наименование * @return $this - * @throws TooLongItemNameException Слишком длинное имя/наименование + * @throws TooLongItemNameException * @throws EmptyItemNameException */ public function setName(string $name): self @@ -186,6 +188,7 @@ class Item extends Entity * @return $this * @throws NegativeItemPriceException * @throws TooHighPriceException + * @throws TooHighSumException */ public function setPrice(float $rubles): self { @@ -217,6 +220,7 @@ class Item extends Entity * @return $this * @throws TooHighItemQuantityException * @throws NegativeItemQuantityException + * @throws TooHighSumException */ public function setQuantity(float $quantity): self { @@ -457,7 +461,7 @@ class Item extends Entity * * @param string|null $user_data Дополнительный реквизит * @return $this - * @throws TooLongUserdataException Слишком длинный дополнительный реквизит + * @throws TooLongUserdataException */ public function setUserData(?string $user_data): self { @@ -485,8 +489,9 @@ class Item extends Entity * @param float|null $excise * @return Item * @throws NegativeItemExciseException + * @throws TooHighSumException */ - public function setExcise(?float $excise): Item + public function setExcise(?float $excise): self { if ($excise < 0) { throw new NegativeItemExciseException($this->getName(), $excise); @@ -558,25 +563,6 @@ class Item extends Entity return $this; } - /** - * Расчитывает стоимость и размер НДС на неё - * - * @return float - * @throws TooHighPriceException Слишком большая сумма - */ - //public function calcSum(): float - //{ - // $sum = $this->quantity * $this->price; - // if (rubles($sum) > 42949672.95) { - // throw new TooHighPriceException($sum, 42949672.95); - // } - // $this->sum = $sum; - // if ($this->vat) { - // $this->vat->setSum(rubles($sum)); - // } - // return $this->getSum(); - //} - /** * @inheritDoc * @throws TooHighSumException diff --git a/src/Entities/Kkt.php b/src/Entities/Kkt.php index 7203951..690c495 100644 --- a/src/Entities/Kkt.php +++ b/src/Entities/Kkt.php @@ -11,14 +11,17 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Exceptions\EmptyMonitorDataException; -use AtolOnline\Exceptions\NotEnoughMonitorDataException; +use AtolOnline\Exceptions\{ + EmptyMonitorDataException, + NotEnoughMonitorDataException +}; use DateTime; use Exception; /** * Класс сущности ККТ, получаемой от монитора * + * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11 * @property string|null serialNumber Заводской номер ККТ * @property string|null registrationNumber Регистрационный номер машины (РНМ) * @property string|null deviceNumber Номер автоматического устройства (внутренний идентификатор устройства) @@ -38,14 +41,13 @@ use Exception; * @property DateTime|string|null firstUnsetDocTimestamp Дата первого неотправленного документа. Указывается, если * есть неотправленные документы. * @property int|null networkErrorCode Код ошибки сети - * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11 */ final class Kkt extends Entity { /** * Сопоставление кодов сетевых ошибок ККТ с их описаниями */ - public const ERROR_CODES = [ + public const ERROR_CODES = [ 0 => 'Нет ошибок', 1 => 'Отсутствует физический канал связи', 2 => 'Ошибка сетевых настроек или нет соединения с сервером ОФД', @@ -97,7 +99,7 @@ final class Kkt extends Entity * @throws EmptyMonitorDataException * @throws NotEnoughMonitorDataException */ - public function __construct(protected \stdClass $data) + public function __construct(protected object $data) { if (empty((array)$data)) { throw new EmptyMonitorDataException(); diff --git a/src/Entities/MoneyTransferOperator.php b/src/Entities/MoneyTransferOperator.php index 198bab8..0047065 100644 --- a/src/Entities/MoneyTransferOperator.php +++ b/src/Entities/MoneyTransferOperator.php @@ -11,9 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Constants\Constraints; -use AtolOnline\Exceptions\InvalidInnLengthException; -use AtolOnline\Exceptions\InvalidPhoneException; +use AtolOnline\Exceptions\{ + InvalidInnLengthException, + InvalidPhoneException +}; +use AtolOnline\Traits\{ + HasInn, + HasPhones +}; use Illuminate\Support\Collection; /** @@ -23,6 +28,8 @@ use Illuminate\Support\Collection; */ class MoneyTransferOperator extends Entity { + use HasInn, HasPhones; + /** * @var string|null Наименование (1026) */ @@ -111,69 +118,6 @@ class MoneyTransferOperator extends Entity return $this; } - /** - * Возвращает установленные номера телефонов - * - * @todo вытащить в трейт - * @return Collection - */ - public function getPhones(): Collection - { - return $this->phones; - } - - /** - * Устанавливает массив номеров телефонов - * - * @todo вытащить в трейт - * @param array|Collection|null $phones - * @return $this - * @throws InvalidPhoneException - */ - public function setPhones(array|Collection|null $phones): self - { - if (!is_null($phones)) { - $phones = is_array($phones) ? collect($phones) : $phones; - $phones->each(function ($phone) { - $phone = preg_replace('/[^\d]/', '', trim($phone)); - if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { - throw new InvalidPhoneException($phone); - } - }); - } - $this->phones = $phones ?? collect(); - return $this; - } - - /** - * Возвращает установленный ИНН - * - * @return string|null - */ - public function getInn(): ?string - { - return $this->inn; - } - - /** - * Устанавливает ИНН - * - * @param string|null $inn - * @return $this - * @throws InvalidInnLengthException Некорректная длина ИНН - */ - public function setInn(?string $inn): self - { - if (is_string($inn)) { - $inn = preg_replace('/[^\d]/', '', trim($inn)); - if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { - throw new InvalidInnLengthException($inn); - } - } - $this->inn = $inn ?: null; - return $this; - } - /** * @inheritDoc */ diff --git a/src/Entities/PayingAgent.php b/src/Entities/PayingAgent.php index 69636c7..a44aba7 100644 --- a/src/Entities/PayingAgent.php +++ b/src/Entities/PayingAgent.php @@ -12,8 +12,11 @@ declare(strict_types = 1); namespace AtolOnline\Entities; use AtolOnline\Constants\Constraints; -use AtolOnline\Exceptions\InvalidPhoneException; -use AtolOnline\Exceptions\TooLongPayingAgentOperationException; +use AtolOnline\Exceptions\{ + InvalidPhoneException, + TooLongPayingAgentOperationException +}; +use AtolOnline\Traits\HasPhones; use Illuminate\Support\Collection; /** @@ -23,16 +26,13 @@ use Illuminate\Support\Collection; */ class PayingAgent extends Entity { + use HasPhones; + /** * @var string|null Наименование операции (1044) */ protected ?string $operation = null; - /** - * @var Collection Телефоны платёжного агента (1073) - */ - protected Collection $phones; - /** * Конструктор * @@ -78,40 +78,6 @@ class PayingAgent extends Entity return $this->operation; } - /** - * Устанавливает массив номеров телефонов - * - * @todo вытащить в трейт - * @param array|Collection|null $phones - * @return $this - * @throws InvalidPhoneException - */ - public function setPhones(array|Collection|null $phones): self - { - if (!is_null($phones)) { - $phones = is_array($phones) ? collect($phones) : $phones; - $phones->each(function ($phone) { - $phone = preg_replace('/[^\d]/', '', trim($phone)); - if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { - throw new InvalidPhoneException($phone); - } - }); - } - $this->phones = empty($phones) ? collect() : $phones; - return $this; - } - - /** - * Возвращает установленные номера телефонов - * - * @todo вытащить в трейт - * @return Collection - */ - public function getPhones(): Collection - { - return $this->phones; - } - /** * @inheritDoc */ diff --git a/src/Entities/ReceivePaymentsOperator.php b/src/Entities/ReceivePaymentsOperator.php index 06b0414..1bc5107 100644 --- a/src/Entities/ReceivePaymentsOperator.php +++ b/src/Entities/ReceivePaymentsOperator.php @@ -11,8 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Constants\Constraints; use AtolOnline\Exceptions\InvalidPhoneException; +use AtolOnline\Traits\HasPhones; use Illuminate\Support\Collection; /** @@ -22,10 +22,7 @@ use Illuminate\Support\Collection; */ class ReceivePaymentsOperator extends Entity { - /** - * @var Collection Телефоны оператора по приёму платежей (1074) - */ - protected Collection $phones; + use HasPhones; /** * Конструктор @@ -33,46 +30,11 @@ class ReceivePaymentsOperator extends Entity * @param array|Collection|null $phones Телефоны оператора по приёму платежей (1074) * @throws InvalidPhoneException */ - public function __construct( - array|Collection|null $phones = null, - ) { + public function __construct(array|Collection|null $phones = null) + { $this->setPhones($phones); } - /** - * Возвращает установленные номера телефонов - * - * @todo вытащить в трейт - * @return Collection - */ - public function getPhones(): Collection - { - return $this->phones; - } - - /** - * Устанавливает массив номеров телефонов - * - * @todo вытащить в трейт - * @param array|Collection|null $phones - * @return $this - * @throws InvalidPhoneException - */ - public function setPhones(array|Collection|null $phones): self - { - if (!is_null($phones)) { - $phones = is_array($phones) ? collect($phones) : $phones; - $phones->each(function ($phone) { - $phone = preg_replace('/[^\d]/', '', trim($phone)); - if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { - throw new InvalidPhoneException($phone); - } - }); - } - $this->phones = empty($phones) ? collect() : $phones; - return $this; - } - /** * @inheritDoc */ diff --git a/src/Entities/Supplier.php b/src/Entities/Supplier.php index 8ddcc40..81d8c28 100644 --- a/src/Entities/Supplier.php +++ b/src/Entities/Supplier.php @@ -11,9 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Constants\Constraints; -use AtolOnline\Exceptions\InvalidInnLengthException; -use AtolOnline\Exceptions\InvalidPhoneException; +use AtolOnline\Exceptions\{ + InvalidInnLengthException, + InvalidPhoneException +}; +use AtolOnline\Traits\{ + HasInn, + HasPhones +}; use Illuminate\Support\Collection; /** @@ -23,21 +28,13 @@ use Illuminate\Support\Collection; */ class Supplier extends Entity { + use HasPhones, HasInn; + /** * @var string|null Наименование (1225) */ protected ?string $name = null; - /** - * @var string|null ИНН (1226) - */ - protected ?string $inn = null; - - /** - * @var Collection Телефоны (1171) - */ - protected Collection $phones; - /** * Конструктор * @@ -80,69 +77,6 @@ class Supplier extends Entity return $this; } - /** - * Возвращает установленные номера телефонов - * - * @todo вытащить в трейт - * @return Collection - */ - public function getPhones(): Collection - { - return $this->phones; - } - - /** - * Устанавливает массив номеров телефонов - * - * @todo вытащить в трейт - * @param array|Collection|null $phones - * @return $this - * @throws InvalidPhoneException - */ - public function setPhones(array|Collection|null $phones): self - { - if (!is_null($phones)) { - $phones = is_array($phones) ? collect($phones) : $phones; - $phones->each(function ($phone) { - $phone = preg_replace('/[^\d]/', '', trim($phone)); - if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { - throw new InvalidPhoneException($phone); - } - }); - } - $this->phones = empty($phones) ? collect() : $phones; - return $this; - } - - /** - * Возвращает установленный ИНН - * - * @return string|null - */ - public function getInn(): ?string - { - return $this->inn; - } - - /** - * Устанавливает ИНН - * - * @param string|null $inn - * @return $this - * @throws InvalidInnLengthException Некорректная длина ИНН - */ - public function setInn(?string $inn): self - { - if (is_string($inn)) { - $inn = preg_replace('/[^\d]/', '', trim($inn)); - if (preg_match_all(Constraints::PATTERN_INN, $inn) === 0) { - throw new InvalidInnLengthException($inn); - } - } - $this->inn = empty($inn) ? null : $inn; - return $this; - } - /** * @inheritDoc */ diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index a4438db..f158a37 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -14,6 +14,10 @@ namespace AtolOnline\Entities; use AtolOnline\Enums\VatTypes; use AtolOnline\Exceptions\InvalidEnumValueException; use AtolOnline\Helpers; +use JetBrains\PhpStorm\{ + ArrayShape, + Pure +}; /** * Класс, описывающий ставку НДС @@ -37,6 +41,7 @@ class Vat extends Entity * * @param string $type Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106) * @param float $rubles Исходная сумма в рублях, от которой нужно расчитать размер НДС + * @throws InvalidEnumValueException */ public function __construct(string $type, float $rubles) { @@ -99,16 +104,16 @@ class Vat extends Entity * @see https://glavkniga.ru/situations/k500734 * @see https://www.b-kontur.ru/nds-kalkuljator-online */ + #[Pure] public function getCalculated(): float { - $kopeks = Helpers::toKop($this->sum); return Helpers::toRub(match ($this->getType()) { - VatTypes::VAT10 => $kopeks * 10 / 100, - VatTypes::VAT18 => $kopeks * 18 / 100, - VatTypes::VAT20 => $kopeks * 20 / 100, - VatTypes::VAT110 => $kopeks * 10 / 110, - VatTypes::VAT118 => $kopeks * 18 / 118, - VatTypes::VAT120 => $kopeks * 20 / 120, + 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, }); } @@ -128,6 +133,8 @@ class Vat extends Entity /** * @inheritDoc */ + #[Pure] + #[ArrayShape(['type' => 'string', 'sum' => 'float'])] public function jsonSerialize(): array { return [ diff --git a/src/Traits/HasEmail.php b/src/Traits/HasEmail.php new file mode 100644 index 0000000..e72b736 --- /dev/null +++ b/src/Traits/HasEmail.php @@ -0,0 +1,57 @@ + Constraints::MAX_LENGTH_EMAIL) { + throw new TooLongEmailException($email); + } elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { + throw new InvalidEmailException($email); + } + } + $this->email = empty($email) ? null : $email; + return $this; + } + + /** + * Возвращает установленный email + * + * @return string|null + */ + public function getEmail(): ?string + { + return $this->email; + } +} diff --git a/src/Traits/HasInn.php b/src/Traits/HasInn.php new file mode 100644 index 0000000..01ae7d3 --- /dev/null +++ b/src/Traits/HasInn.php @@ -0,0 +1,53 @@ +inn = empty($inn) ? null : $inn; + return $this; + } + + /** + * Возвращает установленный ИНН + * + * @return string|null + */ + public function getInn(): ?string + { + return $this->inn; + } +} diff --git a/src/Traits/HasPhones.php b/src/Traits/HasPhones.php new file mode 100644 index 0000000..a6eb5e9 --- /dev/null +++ b/src/Traits/HasPhones.php @@ -0,0 +1,57 @@ +each(function ($phone) { + $phone = preg_replace('/[^\d]/', '', trim($phone)); + if (preg_match(Constraints::PATTERN_PHONE, $phone) != 1) { + throw new InvalidPhoneException($phone); + } + }); + } + $this->phones = empty($phones) ? collect() : $phones; + return $this; + } + + /** + * Возвращает установленные номера телефонов + * + * @return Collection + */ + public function getPhones(): Collection + { + return $this->phones; + } +} From c077f98cf90a826c794d3cc026367ba4a4a3ecaf Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 18:24:00 +0800 Subject: [PATCH 49/85] =?UTF-8?q?`Item`=20-=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D1=87=D1=91=D1=82=20=D0=9D=D0=94=D0=A1=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D0=B8?= =?UTF-8?q?=20=D1=86=D0=B5=D0=BD=D1=8B,=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B2=D0=B0=20=D0=B8=20=D0=B0=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Item.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 7fcc45e..010ce62 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -199,7 +199,7 @@ class Item extends Entity throw new NegativeItemPriceException($this->getName(), $rubles); } $this->price = $rubles; - //$this->calcSum(); + $this->getVat()?->setSum($this->getSum()); return $this; } @@ -232,6 +232,7 @@ class Item extends Entity throw new NegativeItemQuantityException($this->getName(), $quantity); } $this->quantity = $quantity; + $this->getVat()?->setSum($this->getSum()); return $this; } @@ -497,6 +498,7 @@ class Item extends Entity throw new NegativeItemExciseException($this->getName(), $excise); } $this->excise = $excise; + $this->getVat()?->setSum($this->getSum()); return $this; } From 3e03fdca610ac90ae7bce842f09bfb18dbf80ffd Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 18:24:21 +0800 Subject: [PATCH 50/85] =?UTF-8?q?=D0=A4=D0=B8=D0=BD=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=D1=81=D0=B5=D1=85=20`En?= =?UTF-8?q?tities`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/AgentInfo.php | 2 +- src/Entities/Client.php | 2 +- src/Entities/Company.php | 2 +- src/Entities/Item.php | 2 +- src/Entities/MoneyTransferOperator.php | 2 +- src/Entities/PayingAgent.php | 2 +- src/Entities/ReceivePaymentsOperator.php | 2 +- src/Entities/Supplier.php | 2 +- src/Entities/Vat.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Entities/AgentInfo.php b/src/Entities/AgentInfo.php index 6b4ddec..4607734 100644 --- a/src/Entities/AgentInfo.php +++ b/src/Entities/AgentInfo.php @@ -19,7 +19,7 @@ use AtolOnline\Exceptions\InvalidEnumValueException; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 26-28 */ -class AgentInfo extends Entity +final class AgentInfo extends Entity { /** * @var string|null Признак агента (1057) diff --git a/src/Entities/Client.php b/src/Entities/Client.php index 4e54f37..983233b 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -30,7 +30,7 @@ use JetBrains\PhpStorm\Pure; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ -class Client extends Entity +final class Client extends Entity { use HasEmail, HasInn; diff --git a/src/Entities/Company.php b/src/Entities/Company.php index aeb556e..9ce6bfe 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -32,7 +32,7 @@ use JetBrains\PhpStorm\ArrayShape; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ -class Company extends Entity +final class Company extends Entity { use HasEmail, HasInn; diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 010ce62..0372fc3 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -40,7 +40,7 @@ use AtolOnline\Exceptions\{ * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21-30 */ -class Item extends Entity +final class Item extends Entity { /** * @var string Наименование (1030) diff --git a/src/Entities/MoneyTransferOperator.php b/src/Entities/MoneyTransferOperator.php index 0047065..1ccdb1e 100644 --- a/src/Entities/MoneyTransferOperator.php +++ b/src/Entities/MoneyTransferOperator.php @@ -26,7 +26,7 @@ use Illuminate\Support\Collection; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 28 */ -class MoneyTransferOperator extends Entity +final class MoneyTransferOperator extends Entity { use HasInn, HasPhones; diff --git a/src/Entities/PayingAgent.php b/src/Entities/PayingAgent.php index a44aba7..4b6c028 100644 --- a/src/Entities/PayingAgent.php +++ b/src/Entities/PayingAgent.php @@ -24,7 +24,7 @@ use Illuminate\Support\Collection; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19 */ -class PayingAgent extends Entity +final class PayingAgent extends Entity { use HasPhones; diff --git a/src/Entities/ReceivePaymentsOperator.php b/src/Entities/ReceivePaymentsOperator.php index 1bc5107..83b52a3 100644 --- a/src/Entities/ReceivePaymentsOperator.php +++ b/src/Entities/ReceivePaymentsOperator.php @@ -20,7 +20,7 @@ use Illuminate\Support\Collection; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19-20 */ -class ReceivePaymentsOperator extends Entity +final class ReceivePaymentsOperator extends Entity { use HasPhones; diff --git a/src/Entities/Supplier.php b/src/Entities/Supplier.php index 81d8c28..579e241 100644 --- a/src/Entities/Supplier.php +++ b/src/Entities/Supplier.php @@ -26,7 +26,7 @@ use Illuminate\Support\Collection; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29 */ -class Supplier extends Entity +final class Supplier extends Entity { use HasPhones, HasInn; diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index f158a37..69f737c 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -24,7 +24,7 @@ use JetBrains\PhpStorm\{ * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25, 31 */ -class Vat extends Entity +final class Vat extends Entity { /** * @var string Тип ставки НДС (1199, 1105, 1104, 1103, 1102, 1107, 1106) From 05fd25e8104720c2a8bd333ac6ce9c548eca8bc1 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 19:06:15 +0800 Subject: [PATCH 51/85] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20`AgentInfoTest?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/AtolOnline/Tests/Entities/AgentInfoTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php index 4c9e2ac..5cb76f1 100644 --- a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php +++ b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php @@ -63,9 +63,9 @@ class AgentInfoTest extends BasicTestCase { $this->assertAtolable(new AgentInfo(null), []); $this->assertAtolable(new AgentInfo(AgentTypes::ANOTHER), ['type' => AgentTypes::ANOTHER]); - $this->assertAtolable(new AgentInfo(paying_agent: new PayingAgent()), []); - $this->assertAtolable(new AgentInfo(money_transfer_operator: new MoneyTransferOperator()), []); - $this->assertAtolable(new AgentInfo(receive_payments_operator: new ReceivePaymentsOperator()), []); + $this->assertAtolable(new AgentInfo(pagent: new PayingAgent()), []); + $this->assertAtolable(new AgentInfo(mt_operator: new MoneyTransferOperator()), []); + $this->assertAtolable(new AgentInfo(rp_operator: new ReceivePaymentsOperator()), []); $this->assertAtolable(new AgentInfo( AgentTypes::ANOTHER, From d533164d1b80988703d403ca7ed4f8a9725b50f8 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 20:09:14 +0800 Subject: [PATCH 52/85] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20`correction=5Finfo`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Constraints.php | 5 + src/Constants/Ffd105Tags.php | 5 + src/Entities/Company.php | 8 +- src/Entities/CorrectionInfo.php | 157 ++++++++++++++++++ src/Entities/todo_CorrectionInfo.php | 134 --------------- src/Enums/Enum.php | 3 +- .../EmptyCorrectionNumberException.php | 23 +++ .../InvalidCorrectionDateException.php | 33 ++++ .../Tests/Entities/CorrectionInfoTest.php | 106 ++++++++++++ 9 files changed, 332 insertions(+), 142 deletions(-) create mode 100644 src/Entities/CorrectionInfo.php delete mode 100644 src/Entities/todo_CorrectionInfo.php create mode 100644 src/Exceptions/EmptyCorrectionNumberException.php create mode 100644 src/Exceptions/InvalidCorrectionDateException.php create mode 100644 tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index aa4bc0e..1606bbd 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -142,6 +142,11 @@ final class Constraints */ const MAX_LENGTH_ITEM_CODE = 32; + /** + * Формат даты документа коррекции + */ + const CORRECTION_DATE_FORMAT = 'd.m.Y'; + /** * Регулярное выражение для валидации строки ИНН * diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index e7ed2b0..32a015a 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -162,6 +162,11 @@ final class Ffd105Tags */ const CORRECTION_TYPE = 1173; + /** + * Дата документа основания для коррекции + */ + const CORRECTION_DATE = 1178; + /** * Сумма по чеку (БСО) наличными */ diff --git a/src/Entities/Company.php b/src/Entities/Company.php index 9ce6bfe..1470d7d 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -66,10 +66,7 @@ final class Company extends Entity string $inn, string $payment_address, ) { - $this->setEmail($email); - $this->setSno($sno); - $this->setInn($inn); - $this->setPaymentAddress($payment_address); + $this->setEmail($email)->setSno($sno)->setInn($inn)->setPaymentAddress($payment_address); } /** @@ -92,8 +89,7 @@ final class Company extends Entity public function setSno(string $sno): self { $sno = trim($sno); - SnoTypes::isValid($sno); - $this->sno = $sno; + SnoTypes::isValid($sno) && $this->sno = $sno; return $this; } diff --git a/src/Entities/CorrectionInfo.php b/src/Entities/CorrectionInfo.php new file mode 100644 index 0000000..5579c94 --- /dev/null +++ b/src/Entities/CorrectionInfo.php @@ -0,0 +1,157 @@ +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(), + ]; + } +} diff --git a/src/Entities/todo_CorrectionInfo.php b/src/Entities/todo_CorrectionInfo.php deleted file mode 100644 index f404f17..0000000 --- a/src/Entities/todo_CorrectionInfo.php +++ /dev/null @@ -1,134 +0,0 @@ -setType($type); - $base_date && $this->setDate($base_date); - $base_number && $this->setNumber($base_number); - } - - /** - * Возвращает номер документа основания для коррекции. - * Тег ФФД - 1179. - * - * @return string|null - */ - public function getNumber(): ?string - { - return $this->base_number; - } - - /** - * Устанавливает номер документа основания для коррекции. - * Тег ФФД - 1179. - * - * @param string $number - * @return $this - */ - public function setNumber(string $number): todoCorrectionInfo - { - $this->base_number = trim($number); - 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): todoCorrectionInfo - { - $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): todoCorrectionInfo - { - $this->type = $type; - return $this; - } - - /** - * @inheritDoc - */ - public function jsonSerialize(): array - { - return [ - 'type' => $this->getType() ?? '', - 'base_date' => $this->getDate() ?? '', - 'base_number' => $this->getNumber() ?? '', - ]; - } -} diff --git a/src/Enums/Enum.php b/src/Enums/Enum.php index 84803ab..3a178f0 100644 --- a/src/Enums/Enum.php +++ b/src/Enums/Enum.php @@ -24,8 +24,7 @@ abstract class Enum extends \MyCLabs\Enum\Enum */ public static function isValid($value) { - return parent::isValid($value) - ?: throw new InvalidEnumValueException(static::class, $value); + return parent::isValid($value) ?: throw new InvalidEnumValueException(static::class, $value); } /** diff --git a/src/Exceptions/EmptyCorrectionNumberException.php b/src/Exceptions/EmptyCorrectionNumberException.php new file mode 100644 index 0000000..1dedbb0 --- /dev/null +++ b/src/Exceptions/EmptyCorrectionNumberException.php @@ -0,0 +1,23 @@ +assertAtolable( + new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', $number = Helpers::randomStr()), + [ + 'type' => CorrectionTypes::SELF, + 'base_date' => '01.01.2021', + 'base_number' => $number, + ] + ); + } + + /** + * Тестирует исключение при некорректном типе + * + * @covers \AtolOnline\Entities\CorrectionInfo + * @covers \AtolOnline\Entities\CorrectionInfo::setType + * @covers \AtolOnline\Enums\CorrectionTypes::isValid + * @covers \AtolOnline\Exceptions\InvalidEnumValueException + * @return void + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + * @throws InvalidEnumValueException + */ + public function testInvalidEnumValueException(): void + { + $this->expectException(InvalidEnumValueException::class); + $this->expectExceptionMessage('Некорректное значение AtolOnline\Enums\CorrectionTypes::wrong_value'); + new CorrectionInfo('wrong_value', '01.01.2021', Helpers::randomStr()); + } + + /** + * Тестирует исключение при некорректной дате + * + * @covers \AtolOnline\Entities\CorrectionInfo + * @covers \AtolOnline\Entities\CorrectionInfo::setDate + * @covers \AtolOnline\Enums\CorrectionTypes::isValid + * @covers \AtolOnline\Exceptions\InvalidCorrectionDateException + * @return void + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + * @throws InvalidEnumValueException + */ + public function testInvalidCorrectionDateException(): void + { + $this->expectException(InvalidCorrectionDateException::class); + new CorrectionInfo(CorrectionTypes::SELF, Helpers::randomStr(), Helpers::randomStr()); + } + + /** + * Тестирует исключение при некорректной дате + * + * @covers \AtolOnline\Entities\CorrectionInfo + * @covers \AtolOnline\Entities\CorrectionInfo::setNumber + * @covers \AtolOnline\Enums\CorrectionTypes::isValid + * @covers \AtolOnline\Exceptions\EmptyCorrectionNumberException + * @return void + */ + public function testEmptyCorrectionNumberException(): void + { + $this->expectException(EmptyCorrectionNumberException::class); + new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', "\n\r\t\0"); + } +} From 65ec6390149a14086e532a4ec49f70ce547a45ba Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 23:52:41 +0800 Subject: [PATCH 53/85] =?UTF-8?q?=D0=9E=D0=BA=D1=80=D1=83=D0=B3=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=86=D0=B5=D0=BD=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82=D0=B2=D0=B0=20?= =?UTF-8?q?=D0=B2=20=D1=81=D0=B5=D1=82=D1=8C=D0=B5=D1=80=D0=B0=D1=85=20`It?= =?UTF-8?q?em`=20=D0=B4=D0=BE=202=20=D0=B8=203=20=D0=B7=D0=BD=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B7=D0=B0=D0=BF=D1=8F=D1=82=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D0=BD=D0=BD=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Также переименованы исключения о слишком высоких цене и сумме предмета расчёта, чтобы избежать конфликтов с другими --- src/Entities/Item.php | 37 +++++----- ...tion.php => TooHighItemPriceException.php} | 4 +- ...eption.php => TooHighItemSumException.php} | 6 +- tests/AtolOnline/Tests/Entities/ItemTest.php | 74 +++++++++---------- 4 files changed, 61 insertions(+), 60 deletions(-) rename src/Exceptions/{TooHighPriceException.php => TooHighItemPriceException.php} (90%) rename src/Exceptions/{TooHighSumException.php => TooHighItemSumException.php} (85%) diff --git a/src/Entities/Item.php b/src/Entities/Item.php index 0372fc3..e82bd7c 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -25,9 +25,9 @@ use AtolOnline\Exceptions\{ NegativeItemExciseException, NegativeItemPriceException, NegativeItemQuantityException, + TooHighItemPriceException, TooHighItemQuantityException, - TooHighPriceException, - TooHighSumException, + TooHighItemSumException, TooLongItemCodeException, TooLongItemNameException, TooLongMeasurementUnitException, @@ -124,7 +124,7 @@ final class Item extends Entity * @param float|null $price Цена за одну единицу * @param float|null $quantity Количество * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -184,21 +184,22 @@ final class Item extends Entity /** * Устанавливает цену в рублях * - * @param float $rubles + * @param float $price * @return $this * @throws NegativeItemPriceException - * @throws TooHighPriceException - * @throws TooHighSumException + * @throws TooHighItemPriceException + * @throws TooHighItemSumException */ - public function setPrice(float $rubles): self + public function setPrice(float $price): self { - if ($rubles > Constraints::MAX_COUNT_ITEM_PRICE) { - throw new TooHighPriceException($this->getName(), $rubles); + $price = round($price, 2); + if ($price > Constraints::MAX_COUNT_ITEM_PRICE) { + throw new TooHighItemPriceException($this->getName(), $price); } - if ($rubles < 0) { - throw new NegativeItemPriceException($this->getName(), $rubles); + if ($price < 0) { + throw new NegativeItemPriceException($this->getName(), $price); } - $this->price = $rubles; + $this->price = $price; $this->getVat()?->setSum($this->getSum()); return $this; } @@ -220,7 +221,7 @@ final class Item extends Entity * @return $this * @throws TooHighItemQuantityException * @throws NegativeItemQuantityException - * @throws TooHighSumException + * @throws TooHighItemSumException */ public function setQuantity(float $quantity): self { @@ -240,13 +241,13 @@ final class Item extends Entity * Возвращает стоимость (цена * количество + акциз) * * @return float - * @throws TooHighSumException + * @throws TooHighItemSumException */ public function getSum(): float { $sum = $this->getPrice() * $this->getQuantity() + (float)$this->getExcise(); if ($sum > Constraints::MAX_COUNT_ITEM_SUM) { - throw new TooHighSumException($this->getName(), $sum); + throw new TooHighItemSumException($this->getName(), $sum); } return $sum; } @@ -386,7 +387,7 @@ final class Item extends Entity * * @param Vat|string|null $vat Объект ставки, одно из значений VatTypes или null для удаления ставки * @return $this - * @throws TooHighSumException + * @throws TooHighItemSumException * @throws InvalidEnumValueException */ public function setVat(Vat|string|null $vat): self @@ -490,7 +491,7 @@ final class Item extends Entity * @param float|null $excise * @return Item * @throws NegativeItemExciseException - * @throws TooHighSumException + * @throws TooHighItemSumException */ public function setExcise(?float $excise): self { @@ -567,7 +568,7 @@ final class Item extends Entity /** * @inheritDoc - * @throws TooHighSumException + * @throws TooHighItemSumException */ public function jsonSerialize(): array { diff --git a/src/Exceptions/TooHighPriceException.php b/src/Exceptions/TooHighItemPriceException.php similarity index 90% rename from src/Exceptions/TooHighPriceException.php rename to src/Exceptions/TooHighItemPriceException.php index 2e2d286..61a813d 100644 --- a/src/Exceptions/TooHighPriceException.php +++ b/src/Exceptions/TooHighItemPriceException.php @@ -15,9 +15,9 @@ use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Ffd105Tags; /** - * Исключение, возникающее при попытке указать слишком высокую цену (сумму) + * Исключение, возникающее при попытке указать слишком высокую цену (сумму) предмета расчёта */ -class TooHighPriceException extends TooManyException +class TooHighItemPriceException extends TooManyException { protected array $ffd_tags = [Ffd105Tags::ITEM_PRICE]; protected float $max = Constraints::MAX_COUNT_ITEM_PRICE; diff --git a/src/Exceptions/TooHighSumException.php b/src/Exceptions/TooHighItemSumException.php similarity index 85% rename from src/Exceptions/TooHighSumException.php rename to src/Exceptions/TooHighItemSumException.php index 9fd4073..b296bee 100644 --- a/src/Exceptions/TooHighSumException.php +++ b/src/Exceptions/TooHighItemSumException.php @@ -15,12 +15,12 @@ use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Ffd105Tags; /** - * Исключение, возникающее при попытке получеиня слишком высокой стоимости + * Исключение, возникающее при попытке получеиня слишком высокой стоимости предмета расчёта */ -class TooHighSumException extends TooManyException +class TooHighItemSumException extends TooManyException { protected array $ffd_tags = [Ffd105Tags::ITEM_SUM]; - protected float $max = Constraints::MAX_COUNT_ITEM_PRICE; + protected float $max = Constraints::MAX_COUNT_ITEM_SUM; /** * Конструктор diff --git a/tests/AtolOnline/Tests/Entities/ItemTest.php b/tests/AtolOnline/Tests/Entities/ItemTest.php index 58a2a86..f55aa2e 100644 --- a/tests/AtolOnline/Tests/Entities/ItemTest.php +++ b/tests/AtolOnline/Tests/Entities/ItemTest.php @@ -31,9 +31,9 @@ use AtolOnline\{ Exceptions\NegativeItemExciseException, Exceptions\NegativeItemPriceException, Exceptions\NegativeItemQuantityException, + Exceptions\TooHighItemPriceException, Exceptions\TooHighItemQuantityException, - Exceptions\TooHighPriceException, - Exceptions\TooHighSumException, + Exceptions\TooHighItemSumException, Exceptions\TooLongItemCodeException, Exceptions\TooLongItemNameException, Exceptions\TooLongMeasurementUnitException, @@ -62,7 +62,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::getSum * @covers \AtolOnline\Entities\Item::jsonSerialize * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -88,7 +88,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setName * @covers \AtolOnline\Exceptions\TooLongItemNameException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -107,7 +107,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setName * @covers \AtolOnline\Exceptions\EmptyItemNameException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -124,9 +124,9 @@ class ItemTest extends BasicTestCase * * @covers \AtolOnline\Entities\Item * @covers \AtolOnline\Entities\Item::setPrice - * @covers \AtolOnline\Exceptions\TooHighPriceException + * @covers \AtolOnline\Exceptions\TooHighItemPriceException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -134,7 +134,7 @@ class ItemTest extends BasicTestCase */ public function testTooHighPriceException(): void { - $this->expectException(TooHighPriceException::class); + $this->expectException(TooHighItemPriceException::class); new Item('test', Constraints::MAX_COUNT_ITEM_PRICE + 0.1, 3); } @@ -143,12 +143,12 @@ class ItemTest extends BasicTestCase * * @covers \AtolOnline\Entities\Item * @covers \AtolOnline\Entities\Item::setPrice - * @covers \AtolOnline\Exceptions\TooHighSumException - * @throws TooHighSumException + * @covers \AtolOnline\Exceptions\TooHighItemSumException + * @throws TooHighItemSumException */ public function testTooHighSumException(): void { - $this->expectException(TooHighSumException::class); + $this->expectException(TooHighItemSumException::class); (new Item('test', Constraints::MAX_COUNT_ITEM_PRICE, Constraints::MAX_COUNT_ITEM_QUANTITY))->getSum(); } @@ -159,7 +159,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setPrice * @covers \AtolOnline\Exceptions\NegativeItemPriceException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -178,7 +178,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setQuantity * @covers \AtolOnline\Exceptions\TooHighItemQuantityException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -197,7 +197,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setQuantity * @covers \AtolOnline\Exceptions\NegativeItemQuantityException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -217,7 +217,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::getMeasurementUnit * @throws EmptyItemNameException * @throws NegativeItemPriceException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooLongMeasurementUnitException * @throws TooManyException @@ -236,7 +236,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooLongMeasurementUnitException * @throws TooManyException @@ -260,7 +260,7 @@ class ItemTest extends BasicTestCase * @throws InvalidEnumValueException * @throws TooManyException * @throws NegativeItemPriceException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws NegativeItemQuantityException * @throws TooLongItemNameException */ @@ -294,7 +294,7 @@ class ItemTest extends BasicTestCase * @throws InvalidEnumValueException * @throws TooManyException * @throws NegativeItemPriceException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws NegativeItemQuantityException * @throws TooLongItemNameException */ @@ -314,7 +314,7 @@ class ItemTest extends BasicTestCase * @throws InvalidEnumValueException * @throws TooManyException * @throws NegativeItemPriceException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws NegativeItemQuantityException * @throws TooLongItemNameException */ @@ -334,7 +334,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException */ @@ -365,7 +365,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException */ @@ -399,7 +399,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException * @throws InvalidEnumValueException @@ -422,7 +422,7 @@ class ItemTest extends BasicTestCase * @throws InvalidPhoneException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooLongPayingAgentOperationException * @throws TooManyException @@ -450,7 +450,7 @@ class ItemTest extends BasicTestCase * @throws InvalidPhoneException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException */ @@ -485,7 +485,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooLongUserdataException * @throws TooManyException @@ -514,7 +514,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::getUserData * @throws EmptyItemNameException * @throws NegativeItemPriceException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException * @throws NegativeItemQuantityException @@ -534,7 +534,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooLongUserdataException * @throws TooManyException @@ -555,7 +555,7 @@ class ItemTest extends BasicTestCase * @throws InvalidOKSMCodeException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException */ @@ -582,7 +582,7 @@ class ItemTest extends BasicTestCase * @throws InvalidOKSMCodeException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException */ @@ -601,7 +601,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException * @throws InvalidDeclarationNumberException @@ -629,7 +629,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws InvalidDeclarationNumberException * @throws TooManyException @@ -649,7 +649,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws InvalidDeclarationNumberException * @throws TooManyException @@ -669,7 +669,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::getSum * @covers \AtolOnline\Entities\Item::jsonSerialize * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -696,7 +696,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setExcise * @covers \AtolOnline\Exceptions\NegativeItemExciseException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException @@ -719,7 +719,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException * @throws TooLongItemCodeException @@ -756,7 +756,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooLongItemCodeException * @throws TooLongItemNameException * @throws TooManyException @@ -774,7 +774,7 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::setCode * @covers \AtolOnline\Exceptions\TooLongItemCodeException * @throws TooLongItemNameException - * @throws TooHighPriceException + * @throws TooHighItemPriceException * @throws TooManyException * @throws NegativeItemPriceException * @throws EmptyItemNameException From 7d0c526663b69a43d1d6e27f48646be70a2f4fd9 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Fri, 3 Dec 2021 23:53:42 +0800 Subject: [PATCH 54/85] =?UTF-8?q?=D0=93=D0=BE=D1=82=D0=BE=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81=20=D0=BE=D0=BF=D0=BB=D0=B0=D1=82=D1=8B?= =?UTF-8?q?=20`Payment`=20=D0=B4=D0=BB=D1=8F=20=D0=B1=D1=83=D0=B4=D1=83?= =?UTF-8?q?=D1=89=D0=B5=D0=B9=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BE=D0=BF=D0=BB=D0=B0=D1=82=20=D0=B2=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Constraints.php | 5 + src/Entities/Payment.php | 119 ++++++++++++++++++ src/Entities/todo_Payment.php | 99 --------------- .../NegativePaymentSumException.php | 38 ++++++ src/Exceptions/TooHighPaymentSumException.php | 31 +++++ .../AtolOnline/Tests/Entities/PaymentTest.php | 105 ++++++++++++++++ tests/AtolOnline/Tests/Entities/VatTest.php | 3 + 7 files changed, 301 insertions(+), 99 deletions(-) create mode 100644 src/Entities/Payment.php delete mode 100644 src/Entities/todo_Payment.php create mode 100644 src/Exceptions/NegativePaymentSumException.php create mode 100644 src/Exceptions/TooHighPaymentSumException.php create mode 100644 tests/AtolOnline/Tests/Entities/PaymentTest.php diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index 1606bbd..49dfec6 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -147,6 +147,11 @@ final class Constraints */ const CORRECTION_DATE_FORMAT = 'd.m.Y'; + /** + * Максимальная сумма одной оплаты + */ + const MAX_COUNT_PAYMENT_SUM = 99999.999; + /** * Регулярное выражение для валидации строки ИНН * diff --git a/src/Entities/Payment.php b/src/Entities/Payment.php new file mode 100644 index 0000000..17381b4 --- /dev/null +++ b/src/Entities/Payment.php @@ -0,0 +1,119 @@ +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(), + ]; + } +} diff --git a/src/Entities/todo_Payment.php b/src/Entities/todo_Payment.php deleted file mode 100644 index 6398a42..0000000 --- a/src/Entities/todo_Payment.php +++ /dev/null @@ -1,99 +0,0 @@ -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): todoPayment - { - $this->type = $type; - return $this; - } - - /** - * Возвращает сумму оплаты - * - * @return float - */ - public function getSum(): float - { - return $this->sum; - } - - /** - * Устанавливает сумму оплаты - * - * @param float $sum - * @return $this - */ - public function setSum(float $sum): todoPayment - { - $this->sum = $sum; - return $this; - } - - /** - * @inheritDoc - */ - public function jsonSerialize() - { - return [ - 'type' => $this->getType(), - 'sum' => $this->getSum(), - ]; - } -} diff --git a/src/Exceptions/NegativePaymentSumException.php b/src/Exceptions/NegativePaymentSumException.php new file mode 100644 index 0000000..1b952ae --- /dev/null +++ b/src/Exceptions/NegativePaymentSumException.php @@ -0,0 +1,38 @@ +assertAtolable( + new Payment(PaymentTypes::ELECTRON, 123.456789), + [ + 'type' => PaymentTypes::ELECTRON, + 'sum' => 123.46, + ] + ); + } + + /** + * Тестирует исключение при некорректном типе + * + * @covers \AtolOnline\Entities\Payment + * @covers \AtolOnline\Entities\Payment::setType + * @covers \AtolOnline\Enums\PaymentTypes::isValid + * @covers \AtolOnline\Exceptions\InvalidEnumValueException + * @return void + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testInvalidEnumValueException(): void + { + $this->expectException(InvalidEnumValueException::class); + $this->expectExceptionMessage('Некорректное значение AtolOnline\Enums\PaymentTypes::123'); + new Payment(123, 123.456789); + } + + /** + * Тестирует исключение при слишком большой сумме + * + * @covers \AtolOnline\Entities\Payment + * @covers \AtolOnline\Entities\Payment::setSum + * @covers \AtolOnline\Exceptions\TooHighPaymentSumException + * @return void + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + */ + public function testTooHighPaymentSumException(): void + { + $this->expectException(TooHighPaymentSumException::class); + new Payment(PaymentTypes::ELECTRON, Constraints::MAX_COUNT_PAYMENT_SUM + 1); + } + + /** + * Тестирует исключение при отрицательной сумме + * + * @covers \AtolOnline\Entities\Payment + * @covers \AtolOnline\Entities\Payment::setSum + * @covers \AtolOnline\Exceptions\NegativePaymentSumException + * @return void + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testNegativePaymentSumException(): void + { + $this->expectException(NegativePaymentSumException::class); + new Payment(PaymentTypes::ELECTRON, -1); + } +} diff --git a/tests/AtolOnline/Tests/Entities/VatTest.php b/tests/AtolOnline/Tests/Entities/VatTest.php index ba62404..98b029f 100644 --- a/tests/AtolOnline/Tests/Entities/VatTest.php +++ b/tests/AtolOnline/Tests/Entities/VatTest.php @@ -12,6 +12,7 @@ namespace AtolOnline\Tests\Entities; use AtolOnline\{ Entities\Vat, Enums\VatTypes, + Exceptions\InvalidEnumValueException, Tests\BasicTestCase }; @@ -69,6 +70,7 @@ class VatTest extends BasicTestCase * @covers \AtolOnline\Entities\Vat::getSum * @covers \AtolOnline\Entities\Vat::getCalculated * @covers \AtolOnline\Entities\Vat::jsonSerialize + * @throws InvalidEnumValueException */ public function testConstructor(string $type, float $sum): void { @@ -90,6 +92,7 @@ class VatTest extends BasicTestCase * @param float $after_minus Результат после -20р * @covers \AtolOnline\Entities\Vat::addSum * @covers \AtolOnline\Entities\Vat::getCalculated + * @throws InvalidEnumValueException */ public function testVatAdd(string $type, float $after_plus, float $after_minus) { From b451c7dc682bbb936a66065ab819f880e3e3647f Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 14:13:48 +0800 Subject: [PATCH 55/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BE=D1=87=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20phpdoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Kkt.php | 40 ++++++++++----------- src/Exceptions/TooManyVatsException.php | 2 +- tests/AtolOnline/Tests/Entities/VatTest.php | 5 ++- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Entities/Kkt.php b/src/Entities/Kkt.php index 690c495..ba33358 100644 --- a/src/Entities/Kkt.php +++ b/src/Entities/Kkt.php @@ -13,8 +13,7 @@ namespace AtolOnline\Entities; use AtolOnline\Exceptions\{ EmptyMonitorDataException, - NotEnoughMonitorDataException -}; + NotEnoughMonitorDataException}; use DateTime; use Exception; @@ -22,25 +21,26 @@ use Exception; * Класс сущности ККТ, получаемой от монитора * * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11 - * @property string|null serialNumber Заводской номер ККТ - * @property string|null registrationNumber Регистрационный номер машины (РНМ) - * @property string|null deviceNumber Номер автоматического устройства (внутренний идентификатор устройства) - * @property DateTime|string|null fiscalizationDate Дата активации (фискализации) ФН с указанием таймзоны - * @property DateTime|string|null fiscalStorageExpiration Дата замены ФН (Срок действия ФН), с указанием таймзоны - * @property int|null signedDocuments Количество подписанных документов в ФН - * @property float|null fiscalStoragePercentageUse Наполненость ФН в % - * @property string|null fiscalStorageINN ИНН компании (указанный в ФН) - * @property string|null fiscalStorageSerialNumber Заводской (серийный) номер ФН - * @property string|null fiscalStoragePaymentAddress Адрес расчёта, указанный в ФН - * @property string|null groupCode Код группы кассы - * @property DateTime|string|null timestamp Время и дата формирования данных, UTC - * @property bool|null isShiftOpened Признак открыта смена (true) или закрыта (false) - * @property int|null shiftNumber Номер смены (или "Номер закрытой смены", когда смена закрыта) - * @property int|null shiftReceipt Номер документа за смену (или "Кол-во чеков закрытой смены", когда смена закрыта) - * @property int|null unsentDocs Количество неотправленных документов. Указывается, если значение отлично от 0. - * @property DateTime|string|null firstUnsetDocTimestamp Дата первого неотправленного документа. Указывается, если + * @property-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 int|null networkErrorCode Код ошибки сети + * @property-read int|null networkErrorCode Код ошибки сети */ final class Kkt extends Entity { diff --git a/src/Exceptions/TooManyVatsException.php b/src/Exceptions/TooManyVatsException.php index 50e89a3..d732568 100644 --- a/src/Exceptions/TooManyVatsException.php +++ b/src/Exceptions/TooManyVatsException.php @@ -14,7 +14,7 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Constraints; /** - * Исключение, возникающее при попытке добавить слишком много ставок НДС в массив + * Исключение, возникающее при попытке добавить слишком много ставок НДС в документ */ class TooManyVatsException extends TooManyException { diff --git a/tests/AtolOnline/Tests/Entities/VatTest.php b/tests/AtolOnline/Tests/Entities/VatTest.php index 98b029f..d1b1ab0 100644 --- a/tests/AtolOnline/Tests/Entities/VatTest.php +++ b/tests/AtolOnline/Tests/Entities/VatTest.php @@ -13,8 +13,7 @@ use AtolOnline\{ Entities\Vat, Enums\VatTypes, Exceptions\InvalidEnumValueException, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; /** * Набор тестов для проверки работы класса ставки НДС @@ -84,7 +83,7 @@ class VatTest extends BasicTestCase } /** - * Тестирует расчёт суммы НДС от исходной суммы 100+20р и 100-20р + * Тестирует расчёт суммы НДС от суммы 100+20р и 100-20р * * @dataProvider providerVatsAdd * @param string $type Тип НДС From a5c88cd7d392957f8897fdaab907cf192608a846 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 14:15:47 +0800 Subject: [PATCH 56/85] =?UTF-8?q?=D0=A4=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=81=D0=BB=D0=B5=D0=B4=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D0=BB=20=D0=B8=D0=B7=20`Helpers`?= =?UTF-8?q?=20=D0=B2=20`BasicTestCase`=20=D0=B8=20=D0=BE=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `isSameClass()` - `checkImplementsInterfaces()` - `checkUsesTraits()` + переписана под наследование --- src/Helpers.php | 63 ----------- tests/AtolOnline/Tests/Api/KktMonitorTest.php | 4 +- tests/AtolOnline/Tests/BasicTestCase.php | 102 ++++++++++++++---- 3 files changed, 84 insertions(+), 85 deletions(-) diff --git a/src/Helpers.php b/src/Helpers.php index 6275a5d..82a316f 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -52,67 +52,4 @@ final class Helpers } return $result; } - - /** - * Проверяет идентичность двух классов - * - * @param object|string $class1 - * @param object|string $class2 - * @return bool - */ - public static function isSameClass(object|string $class1, object|string $class2): bool - { - return (is_object($class1) ? $class1::class : $class1) === (is_object($class2) ? $class2::class : $class2); - } - - /** - * Тестирует наследование класса (объекта) от указанных классов - * - * @param object|string $class Объект или имя класса для проверки - * @param string[] $parents Имена классов-родителей - * @see https://www.php.net/manual/ru/function.class-parents.php - */ - public static function checkExtendsClasses(object|string $class, array $parents): bool - { - return self::checkClassesIntersection($parents, $class, 'class_parents'); - } - - /** - * Тестирует имплементацию классом (объектом) указанных интерфейсов - * - * @param object|string $actual Объект или имя класса для проверки - * @param string[] $interfaces Имена классов-интерфейсов - * @see https://www.php.net/manual/ru/function.class-implements.php - */ - public static function checkImplementsInterfaces(object|string $actual, array $interfaces): bool - { - return self::checkClassesIntersection($interfaces, $actual, 'class_implements'); - } - - /** - * Тестирует использование классом (объектом) указанных трейтов - * - * @param object|string $class - * @param string[] $traits - * @return bool - * @see https://www.php.net/manual/ru/function.class-uses.php - */ - public static function checkUsesTraits(array $traits, object|string $class): bool - { - return self::checkClassesIntersection($traits, $class, 'class_uses'); - } - - /** - * Проверяет пересечение классов указанной функцией SPL - * - * @param object|string $class Класс для проверки на вхождение, или объект, класс коего нужно проверить - * @param string[] $classes Массив классов, вхождение в который нужно проверить - * @param string $function class_parents|class_implements|class_uses - */ - protected static function checkClassesIntersection(array $classes, object|string $class, string $function): bool - { - $actual_classes = is_object($class) ? $function($class) : [$class::class]; - return is_array($actual_classes) - && !empty(array_intersect($classes, $actual_classes)); - } } diff --git a/tests/AtolOnline/Tests/Api/KktMonitorTest.php b/tests/AtolOnline/Tests/Api/KktMonitorTest.php index f5ccfe5..a6d1c9d 100644 --- a/tests/AtolOnline/Tests/Api/KktMonitorTest.php +++ b/tests/AtolOnline/Tests/Api/KktMonitorTest.php @@ -57,7 +57,7 @@ class KktMonitorTest extends BasicTestCase $client = new KktMonitor(); $this->assertIsObject($client); $this->assertIsSameClass(KktMonitor::class, $client); - $this->assertExtendsClasses($client, [AtolClient::class]); + $this->assertExtendsClasses([AtolClient::class], $client); } /** @@ -78,7 +78,7 @@ class KktMonitorTest extends BasicTestCase $client = new KktMonitor(false, 'login', 'password', []); $this->assertIsObject($client); $this->assertIsSameClass($client, KktMonitor::class); - $this->assertExtendsClasses($client, [AtolClient::class]); + $this->assertExtendsClasses([AtolClient::class], $client); } /** diff --git a/tests/AtolOnline/Tests/BasicTestCase.php b/tests/AtolOnline/Tests/BasicTestCase.php index 3d1f26d..3e00662 100644 --- a/tests/AtolOnline/Tests/BasicTestCase.php +++ b/tests/AtolOnline/Tests/BasicTestCase.php @@ -81,60 +81,122 @@ class BasicTestCase extends TestCase } } + //------------------------------------------------------------------------------------------------------------------ + /** * Тестирует идентичность двух классов * - * @param object|string $expected - * @param object|string $actual + * @param object|string $expected Ожидаемый класс + * @param object|string $actual Фактический класс */ public function assertIsSameClass(object|string $expected, object|string $actual): void { - $this->assertTrue(Helpers::isSameClass($expected, $actual)); + $this->assertTrue($this->checkisSameClass($expected, $actual)); + } + + /** + * Проверяет идентичность двух классов + * + * @param object|string $class1 + * @param object|string $class2 + * @return bool + */ + private function checkisSameClass(object|string $class1, object|string $class2): bool + { + return (is_object($class1) ? $class1::class : $class1) + === (is_object($class2) ? $class2::class : $class2); } /** * Тестирует наследование класса (объекта) от указанных классов * - * @param object|string $class - * @param string[] $parents + * @param array $expected Массив ожидаемых имён классов-родителей + * @param object|string $actual Объект или имя класса для проверки */ - public function assertExtendsClasses(object|string $class, array $parents): void + public function assertExtendsClasses(array $expected, object|string $actual): void { - $this->assertTrue(Helpers::checkExtendsClasses($class, $parents)); + $this->assertTrue($this->checkExtendsClasses($expected, $actual)); + } + + /** + * Проверяет наследование класса (объекта) от указанных классов + * + * @param string[] $parents Имена классов-родителей + * @param object|string $class Объект или имя класса для проверки + */ + private function checkExtendsClasses(array $parents, object|string $class): bool + { + return !empty(array_intersect($parents, is_object($class) ? class_parents($class) : [$class])); } /** * Тестирует имплементацию классом (объектом) указанных интерфейсов * - * @param object|string $class - * @param string[] $interfaces + * @param string[] $expected Массив ожидаемых имён интерфейсов + * @param object|string $actual Объект или имя класса для проверки */ - public function assertImplementsInterfaces(object|string $class, array $interfaces): void + public function assertImplementsInterfaces(array $expected, object|string $actual): void { - $this->assertTrue(Helpers::checkImplementsInterfaces($class, $interfaces)); + $this->assertTrue($this->checkImplementsInterfaces($expected, $actual)); + } + + /** + * Проверяет имплементацию классом (объектом) указанных интерфейсов + * + * @param string[] $interfaces Имена классов-интерфейсов + * @param object|string $class Объект или имя класса для проверки + * @see https://www.php.net/manual/ru/function.class-implements.php + */ + private function checkImplementsInterfaces(array $interfaces, object|string $class): bool + { + return !empty(array_intersect($interfaces, is_object($class) ? class_implements($class) : [$class])); } /** * Тестирует использование классом (объектом) указанных трейтов * - * @param object|string $class - * @param string[] $traits + * @param string[] $expected Массив ожидаемых имён трейтов + * @param object|string $actual Объект или имя класса для проверки */ - public function assertUsesTraits(object|string $class, array $traits): void + public function assertUsesTraits(array $expected, object|string $actual): void { - $this->assertTrue(Helpers::checkUsesTraits($traits, $class)); + $this->assertTrue($this->checkUsesTraits($expected, $actual)); + } + + /** + * Проверяет использование классом (объектом) указанных трейтов (исключает родителей) + * + * @param string[] $traits Массив ожидаемых имён трейтов + * @param object|string $class Объект или имя класса для проверки + * @return bool + * @see https://www.php.net/manual/ru/function.class-uses.php#110752 + */ + private function checkUsesTraits(array $traits, object|string $class): bool + { + $found_traits = []; + $check_class = is_object($class) ? $class::class : $class; + do { + $found_traits = array_merge(class_uses($check_class, true), $found_traits); + } while ($check_class = get_parent_class($check_class)); + foreach ($found_traits as $trait => $same) { + $found_traits = array_merge(class_uses($trait, true), $found_traits); + } + return !empty(array_intersect(array_unique($found_traits), $traits)); } /** * Тестирует, является ли объект коллекцией * - * @param mixed $expected + * @param mixed $value */ - public function assertIsCollection(mixed $expected): void + public function assertIsCollection(mixed $value): void { - $this->assertIsObject($expected); - $this->assertIsIterable($expected); - $this->assertIsSameClass($expected, Collection::class); + $this->assertIsObject($value); + $this->assertIsIterable($value); + $this->assertTrue( + $this->checkisSameClass(Collection::class, $value) || + $this->checkExtendsClasses([Collection::class], $value) + ); } //------------------------------------------------------------------------------------------------------------------ From 3d3eba5b4ed8c77d716bde0ca574d05b33432736 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 14:16:34 +0800 Subject: [PATCH 57/85] =?UTF-8?q?=D0=91=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=20=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BE=D0=B1=D1=8A=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D0=B2=20`EntityCollection`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/EntityCollection.php | 119 ++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/Entities/EntityCollection.php diff --git a/src/Entities/EntityCollection.php b/src/Entities/EntityCollection.php new file mode 100644 index 0000000..e74557f --- /dev/null +++ b/src/Entities/EntityCollection.php @@ -0,0 +1,119 @@ +checkCount($items); + parent::__construct($items); + } + + /** + * @inheritDoc + */ + public function prepend($value, $key = null): self + { + $this->checkCount(); + return parent::prepend($value, $key); + } + + /** + * @inheritDoc + */ + public function add($item): self + { + $this->checkCount(); + return parent::add($item); + } + + /** + * @inheritDoc + */ + public function push(...$values): self + { + $this->checkCount(); + return parent::push(...$values); + } + + /** + * @inheritDoc + */ + public function merge($items): self + { + $this->checkCount(); + return parent::merge($items); + } + + /** + * @inheritDoc + * @throws \Exception + */ + public function jsonSerialize(): array + { + return array_map(function ($value) { + $this->checkEntityClass($value); + if ($value instanceof \JsonSerializable) { + return $value->jsonSerialize(); + } elseif ($value instanceof Jsonable) { + return json_decode($value->toJson(), true); + } elseif ($value instanceof Arrayable) { + return $value->toArray(); + } + return $value; + }, $this->all()); + } + + /** + * Проверяет количество ставок + * + * @param array $items Массив элементов, если пустой - проверит содержимое коллекции + * @return void + */ + private function checkCount(array $items = []): void + { + if ( + count($items) > static::MAX_COUNT || + $this->count() === static::MAX_COUNT + ) { + $exception = static::EXCEPTION_CLASS; + throw new $exception(static::MAX_COUNT); + } + } + + /** + * @throws \Exception + */ + private function checkEntityClass(mixed $item): void + { + if (!is_object($item) || $item::class !== static::ENTITY_CLASS) { + //TODO proper exception + throw new \Exception( + 'Коллекция должна содержать только объекты класса ' . + static::ENTITY_CLASS . ', найден ' . $item::class + ); + } + } +} From 6b5a0250519b8b707121f8a9ebb3d7bff00f0bef Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 14:16:50 +0800 Subject: [PATCH 58/85] =?UTF-8?q?=D0=9A=D0=BB=D0=B0=D1=81=D1=81=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B8=20=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=B2=D0=BE=D0=BA=20=D0=9D=D0=94=D0=A1=20`Vats`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Vats.php | 29 +++ tests/AtolOnline/Tests/Entities/VatsTest.php | 197 +++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/Entities/Vats.php create mode 100644 tests/AtolOnline/Tests/Entities/VatsTest.php diff --git a/src/Entities/Vats.php b/src/Entities/Vats.php new file mode 100644 index 0000000..e8b52f7 --- /dev/null +++ b/src/Entities/Vats.php @@ -0,0 +1,29 @@ +generateObjects(3)); + $this->assertIsCollection($vats); + $this->assertEquals(3, $vats->count()); + } + + /** + * Тестирует выброс исключения при установке слишком большого количества ставок через конструктор + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyVatsException + * @throws InvalidEnumValueException + */ + public function testTooManyVatsExceptionByConstructor() + { + $this->expectException(TooManyVatsException::class); + new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1)); + } + + /** + * Тестирует добавление ставки в начало коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::prepend + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @throws InvalidEnumValueException + */ + public function testPrepend() + { + $vats = (new Vats($this->generateObjects(3))) + ->prepend($this->generateObjects()); + $this->assertEquals(4, $vats->count()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::prepend + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyVatsException + * @throws InvalidEnumValueException + */ + public function testTooManyVatsExceptionByPrepend() + { + $this->expectException(TooManyVatsException::class); + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1))) + ->prepend($this->generateObjects()); + } + + /** + * Тестирует добавление ставки в конец коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::add + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @throws InvalidEnumValueException + */ + public function testAdd() + { + $vats = (new Vats($this->generateObjects(3))) + ->add($this->generateObjects()); + $this->assertEquals(4, $vats->count()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::add + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyVatsException + * @throws InvalidEnumValueException + */ + public function testTooManyVatsExceptionByAdd() + { + $this->expectException(TooManyVatsException::class); + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1))) + ->add($this->generateObjects()); + } + + /** + * Тестирует добавление лишних ставок в конец коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::push + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @throws InvalidEnumValueException + */ + public function testPush() + { + $vats = (new Vats($this->generateObjects(3))) + ->push(...$this->generateObjects(3)); + $this->assertEquals(6, $vats->count()); + } + + /** + * Тестирует выброс исключения при добавлении лишних ставок в конец коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::push + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyVatsException + * @throws InvalidEnumValueException + */ + public function testTooManyVatsExceptionByPush() + { + $this->expectException(TooManyVatsException::class); + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1))) + ->push(...$this->generateObjects()); + } + + /** + * Тестирует добавление ставки в начало коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::merge + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @throws InvalidEnumValueException + */ + public function testMerge() + { + $vats = (new Vats($this->generateObjects(3))) + ->merge($this->generateObjects(3)); + $this->assertEquals(6, $vats->count()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::merge + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyVatsException + * @throws InvalidEnumValueException + */ + public function testTooManyVatsExceptionByMerge() + { + $this->expectException(TooManyVatsException::class); + (new Vats($this->generateObjects(9))) + ->merge($this->generateObjects(2)); + } + + /** + * Генерирует массив тестовых объектов ставок НДС + * + * @throws InvalidEnumValueException + * @throws Exception + */ + protected function generateObjects(int $count = 1): array + { + $types = VatTypes::toArray(); + $result = []; + for ($i = 0; $i < abs($count); ++$i) { + $result[] = new Vat( + array_values($types)[random_int(0, count($types) - 1)], + random_int(1, 100) * 2 / 3 + ); + } + return $result; + } +} From bf09641c8bc99f3c7dd78a4587ecfd034c3e2d75 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 14:16:58 +0800 Subject: [PATCH 59/85] =?UTF-8?q?=D0=9A=D0=BB=D0=B0=D1=81=D1=81=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B8=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=BB=D0=B0=D1=82=20`Payments`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Payments.php | 34 +++++ .../Tests/Entities/PaymentsTest.php | 136 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/Entities/Payments.php create mode 100644 tests/AtolOnline/Tests/Entities/PaymentsTest.php diff --git a/src/Entities/Payments.php b/src/Entities/Payments.php new file mode 100644 index 0000000..413b20b --- /dev/null +++ b/src/Entities/Payments.php @@ -0,0 +1,34 @@ +expectException(TooManyPaymentsException::class); + new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1)); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции + * + * @covers \AtolOnline\Entities\EntityCollection::prepend + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyPaymentsException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testTooManyPaymentsExceptionByPrepend() + { + $this->expectException(TooManyPaymentsException::class); + (new Payments($this->generateObjects(10))) + ->prepend($this->generateObjects()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции + * + * @covers \AtolOnline\Entities\Payments + * @covers \AtolOnline\Entities\Payments::add + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyPaymentsException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testTooManyPaymentsExceptionByAdd() + { + $this->expectException(TooManyPaymentsException::class); + (new Payments($this->generateObjects(10))) + ->add($this->generateObjects()); + } + + /** + * Тестирует выброс исключения при добавлении лишних оплат в конец коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::push + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyPaymentsException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testTooManyPaymentsExceptionByPush() + { + $this->expectException(TooManyPaymentsException::class); + (new Payments($this->generateObjects(10))) + ->push(...$this->generateObjects()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции + * + * @covers \AtolOnline\Entities\EntityCollection + * @covers \AtolOnline\Entities\EntityCollection::merge + * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyPaymentsException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testTooManyPaymentsExceptionByMerge() + { + $this->expectException(TooManyPaymentsException::class); + (new Payments($this->generateObjects(9))) + ->merge($this->generateObjects(2)); + } + + /** + * Генерирует массив тестовых объектов оплаты + * + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws Exception + */ + protected function generateObjects(int $count = 1): array + { + $types = PaymentTypes::toArray(); + $result = []; + for ($i = 0; $i < abs($count); ++$i) { + $result[] = new Payment( + array_values($types)[random_int(min($types), max($types))], + random_int(1, 100) * 2 / 3 + ); + } + return $result; + } +} From 557c76fefa7bab7f161ac9ef630e1ef9127f3024 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 16:14:19 +0800 Subject: [PATCH 60/85] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B8=20=D0=BD=D0=B5=20=D1=82=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=BA=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - коллекция `Items` с покрытием - вынос коллекций из `AtolOnline\Entities` в `AtolOnline\Collections` - фикс ] в `AtolException` - финализирован `CorrectionInfo` - фиксы по тестам коллекций - прочие мелочи по phpdoc --- .../EntityCollection.php | 45 ++--- src/Collections/Items.php | 35 ++++ src/{Entities => Collections}/Payments.php | 3 +- src/{Entities => Collections}/Vats.php | 10 +- src/Entities/Company.php | 14 +- src/Entities/CorrectionInfo.php | 8 +- src/Entities/Payment.php | 14 +- src/Exceptions/AtolException.php | 2 +- .../InvalidEntityInCollectionException.php | 37 +++++ tests/AtolOnline/Tests/Api/KktMonitorTest.php | 2 + tests/AtolOnline/Tests/BasicTestCase.php | 12 +- .../Tests/Collections/ItemsTest.php | 156 ++++++++++++++++++ .../PaymentsTest.php | 40 ++--- .../{Entities => Collections}/VatsTest.php | 113 +++++++++---- .../Tests/Entities/AgentInfoTest.php | 6 +- .../AtolOnline/Tests/Entities/ClientTest.php | 3 + .../AtolOnline/Tests/Entities/CompanyTest.php | 20 +++ .../Tests/Entities/CorrectionInfoTest.php | 5 +- tests/AtolOnline/Tests/Entities/ItemTest.php | 18 +- .../Entities/MoneyTransferOperatorTest.php | 2 + .../Tests/Entities/PayingAgentTest.php | 2 + .../AtolOnline/Tests/Entities/PaymentTest.php | 8 +- .../Entities/ReceivePaymentsOperatorTest.php | 2 + .../Tests/Entities/SupplierTest.php | 2 + tests/AtolOnline/Tests/Entities/VatTest.php | 2 + 25 files changed, 441 insertions(+), 120 deletions(-) rename src/{Entities => Collections}/EntityCollection.php (57%) create mode 100644 src/Collections/Items.php rename src/{Entities => Collections}/Payments.php (94%) rename src/{Entities => Collections}/Vats.php (72%) create mode 100644 src/Exceptions/InvalidEntityInCollectionException.php create mode 100644 tests/AtolOnline/Tests/Collections/ItemsTest.php rename tests/AtolOnline/Tests/{Entities => Collections}/PaymentsTest.php (76%) rename tests/AtolOnline/Tests/{Entities => Collections}/VatsTest.php (59%) diff --git a/src/Entities/EntityCollection.php b/src/Collections/EntityCollection.php similarity index 57% rename from src/Entities/EntityCollection.php rename to src/Collections/EntityCollection.php index e74557f..74e4c77 100644 --- a/src/Entities/EntityCollection.php +++ b/src/Collections/EntityCollection.php @@ -9,16 +9,14 @@ declare(strict_types = 1); -namespace AtolOnline\Entities; +namespace AtolOnline\Collections; -use Illuminate\Contracts\Support\Arrayable; -use Illuminate\Contracts\Support\Jsonable; +use AtolOnline\Exceptions\InvalidEntityInCollectionException; +use Exception; use Illuminate\Support\Collection; /** * Абстрактное описание коллекции любых сущностей - * - * @todo вот бы ещё проверять классы добавляемых объектов через static.... ммм мякотка */ abstract class EntityCollection extends Collection { @@ -69,21 +67,14 @@ abstract class EntityCollection extends Collection /** * @inheritDoc - * @throws \Exception + * @throws Exception */ public function jsonSerialize(): array { - return array_map(function ($value) { - $this->checkEntityClass($value); - if ($value instanceof \JsonSerializable) { - return $value->jsonSerialize(); - } elseif ($value instanceof Jsonable) { - return json_decode($value->toJson(), true); - } elseif ($value instanceof Arrayable) { - return $value->toArray(); - } - return $value; - }, $this->all()); + $this->each(function ($item) { + $this->checkClass($item); + }); + return parent::jsonSerialize(); } /** @@ -94,26 +85,20 @@ abstract class EntityCollection extends Collection */ private function checkCount(array $items = []): void { - if ( - count($items) > static::MAX_COUNT || - $this->count() === static::MAX_COUNT - ) { - $exception = static::EXCEPTION_CLASS; - throw new $exception(static::MAX_COUNT); + if (count($items) > static::MAX_COUNT || $this->count() === static::MAX_COUNT) { + throw new (static::EXCEPTION_CLASS)(static::MAX_COUNT); } } /** - * @throws \Exception + * Проверяет корректность класса объекта + * + * @throws InvalidEntityInCollectionException */ - private function checkEntityClass(mixed $item): void + private function checkClass(mixed $item): void { if (!is_object($item) || $item::class !== static::ENTITY_CLASS) { - //TODO proper exception - throw new \Exception( - 'Коллекция должна содержать только объекты класса ' . - static::ENTITY_CLASS . ', найден ' . $item::class - ); + throw new InvalidEntityInCollectionException(static::class, static::ENTITY_CLASS, $item); } } } diff --git a/src/Collections/Items.php b/src/Collections/Items.php new file mode 100644 index 0000000..00e740c --- /dev/null +++ b/src/Collections/Items.php @@ -0,0 +1,35 @@ + "string", - 'sno' => "string", - 'inn' => "string", - 'payment_address' => "string", + 'email' => 'string', + 'sno' => 'string', + 'inn' => 'string', + 'payment_address' => 'string', ])] public function jsonSerialize(): array { diff --git a/src/Entities/CorrectionInfo.php b/src/Entities/CorrectionInfo.php index 5579c94..2402640 100644 --- a/src/Entities/CorrectionInfo.php +++ b/src/Entities/CorrectionInfo.php @@ -16,21 +16,19 @@ use AtolOnline\Enums\CorrectionTypes; use AtolOnline\Exceptions\{ EmptyCorrectionNumberException, InvalidCorrectionDateException, - InvalidEnumValueException -}; + InvalidEnumValueException}; use DateTime; use Exception; use JetBrains\PhpStorm\{ ArrayShape, - Pure -}; + Pure}; /** * Класс, описывающий данные коррекции * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35 */ -class CorrectionInfo extends Entity +final class CorrectionInfo extends Entity { /** * @var string|null Тип коррекции (1173) diff --git a/src/Entities/Payment.php b/src/Entities/Payment.php index 17381b4..2982833 100644 --- a/src/Entities/Payment.php +++ b/src/Entities/Payment.php @@ -13,11 +13,13 @@ namespace AtolOnline\Entities; use AtolOnline\Constants\Constraints; use AtolOnline\Enums\PaymentTypes; -use AtolOnline\Exceptions\InvalidEnumValueException; -use AtolOnline\Exceptions\NegativePaymentSumException; -use AtolOnline\Exceptions\TooHighPaymentSumException; -use JetBrains\PhpStorm\ArrayShape; -use JetBrains\PhpStorm\Pure; +use AtolOnline\Exceptions\{ + InvalidEnumValueException, + NegativePaymentSumException, + TooHighPaymentSumException,}; +use JetBrains\PhpStorm\{ + ArrayShape, + Pure}; /** * Класс, описывающий оплату @@ -41,9 +43,9 @@ class Payment extends Entity * * @param int $type Тип оплаты * @param float $sum Сумма оплаты - * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws InvalidEnumValueException */ public function __construct(int $type, float $sum) { diff --git a/src/Exceptions/AtolException.php b/src/Exceptions/AtolException.php index 6b86648..6a7f34e 100644 --- a/src/Exceptions/AtolException.php +++ b/src/Exceptions/AtolException.php @@ -33,7 +33,7 @@ class AtolException extends Exception { $tags = implode(', ', $ffd_tags ?: $this->ffd_tags); parent::__construct( - ($message ?: $this->message) . ($tags ? ' [Теги ФФД: ' . $tags : '') . ']' + ($message ?: $this->message) . ($tags ? ' [Теги ФФД: ' . $tags . ']' : '') ); } } diff --git a/src/Exceptions/InvalidEntityInCollectionException.php b/src/Exceptions/InvalidEntityInCollectionException.php new file mode 100644 index 0000000..f58cd2c --- /dev/null +++ b/src/Exceptions/InvalidEntityInCollectionException.php @@ -0,0 +1,37 @@ +assertIsArray($entity->jsonSerialize()); $this->assertIsString((string)$entity); @@ -204,7 +208,7 @@ class BasicTestCase extends TestCase /** * Провайдер строк, которые приводятся к null * - * @return array> + * @return array */ public function providerNullableStrings(): array { diff --git a/tests/AtolOnline/Tests/Collections/ItemsTest.php b/tests/AtolOnline/Tests/Collections/ItemsTest.php new file mode 100644 index 0000000..7289f6e --- /dev/null +++ b/tests/AtolOnline/Tests/Collections/ItemsTest.php @@ -0,0 +1,156 @@ +expectException(TooManyItemsException::class); + new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS + 1)); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции + * + * @covers \AtolOnline\Collections\EntityCollection::prepend + * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyItemsException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testTooManyItemsExceptionByPrepend() + { + $this->expectException(TooManyItemsException::class); + (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->prepend($this->generateObjects()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции + * + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::add + * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyItemsException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testTooManyItemsExceptionByAdd() + { + $this->expectException(TooManyItemsException::class); + (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->add($this->generateObjects()); + } + + /** + * Тестирует выброс исключения при добавлении лишних оплат в конец коллекции + * + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::push + * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyItemsException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testTooManyItemsExceptionByPush() + { + $this->expectException(TooManyItemsException::class); + (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->push(...$this->generateObjects()); + } + + /** + * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции + * + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::merge + * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Exceptions\TooManyItemsException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testTooManyItemsExceptionByMerge() + { + $this->expectException(TooManyItemsException::class); + (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->merge($this->generateObjects(2)); + } + + /** + * Генерирует массив тестовых объектов предметов расчёта + * + * @param int $count + * @return Item[] + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws Exception + */ + protected function generateObjects(int $count = 1): array + { + $result = []; + for ($i = 0; $i < abs($count); ++$i) { + $result[] = new Item(Helpers::randomStr(), random_int(1, 100), random_int(1, 10)); + } + return $result; + } +} diff --git a/tests/AtolOnline/Tests/Entities/PaymentsTest.php b/tests/AtolOnline/Tests/Collections/PaymentsTest.php similarity index 76% rename from tests/AtolOnline/Tests/Entities/PaymentsTest.php rename to tests/AtolOnline/Tests/Collections/PaymentsTest.php index 7e71a8f..a10d679 100644 --- a/tests/AtolOnline/Tests/Entities/PaymentsTest.php +++ b/tests/AtolOnline/Tests/Collections/PaymentsTest.php @@ -7,12 +7,12 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests\Entities; +namespace AtolOnline\Tests\Collections; use AtolOnline\{ + Collections\Payments, Constants\Constraints, Entities\Payment, - Entities\Payments, Enums\PaymentTypes, Exceptions\InvalidEnumValueException, Exceptions\NegativePaymentSumException, @@ -29,8 +29,8 @@ class PaymentsTest extends BasicTestCase /** * Тестирует выброс исключения при установке слишком большого количества оплат через конструктор * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyPaymentsException * @throws InvalidEnumValueException * @throws NegativePaymentSumException @@ -45,8 +45,8 @@ class PaymentsTest extends BasicTestCase /** * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции * - * @covers \AtolOnline\Entities\EntityCollection::prepend - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection::prepend + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyPaymentsException * @throws InvalidEnumValueException * @throws NegativePaymentSumException @@ -55,16 +55,16 @@ class PaymentsTest extends BasicTestCase public function testTooManyPaymentsExceptionByPrepend() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(10))) + (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) ->prepend($this->generateObjects()); } /** * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции * - * @covers \AtolOnline\Entities\Payments - * @covers \AtolOnline\Entities\Payments::add - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::add + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyPaymentsException * @throws InvalidEnumValueException * @throws NegativePaymentSumException @@ -73,16 +73,16 @@ class PaymentsTest extends BasicTestCase public function testTooManyPaymentsExceptionByAdd() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(10))) + (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) ->add($this->generateObjects()); } /** * Тестирует выброс исключения при добавлении лишних оплат в конец коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::push - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::push + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyPaymentsException * @throws InvalidEnumValueException * @throws NegativePaymentSumException @@ -91,16 +91,16 @@ class PaymentsTest extends BasicTestCase public function testTooManyPaymentsExceptionByPush() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(10))) + (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1))) ->push(...$this->generateObjects()); } /** * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::merge - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::merge + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyPaymentsException * @throws InvalidEnumValueException * @throws NegativePaymentSumException @@ -109,13 +109,15 @@ class PaymentsTest extends BasicTestCase public function testTooManyPaymentsExceptionByMerge() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(9))) + (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS - 1))) ->merge($this->generateObjects(2)); } /** * Генерирует массив тестовых объектов оплаты * + * @param int $count + * @return Payment[] * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException diff --git a/tests/AtolOnline/Tests/Entities/VatsTest.php b/tests/AtolOnline/Tests/Collections/VatsTest.php similarity index 59% rename from tests/AtolOnline/Tests/Entities/VatsTest.php rename to tests/AtolOnline/Tests/Collections/VatsTest.php index 516df86..288fe61 100644 --- a/tests/AtolOnline/Tests/Entities/VatsTest.php +++ b/tests/AtolOnline/Tests/Collections/VatsTest.php @@ -7,14 +7,19 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -namespace AtolOnline\Tests\Entities; +namespace AtolOnline\Tests\Collections; use AtolOnline\{ + Collections\Vats, Constants\Constraints, + Entities\Payment, Entities\Vat, - Entities\Vats, + Enums\PaymentTypes, Enums\VatTypes, + Exceptions\InvalidEntityInCollectionException, Exceptions\InvalidEnumValueException, + Exceptions\NegativePaymentSumException, + Exceptions\TooHighPaymentSumException, Exceptions\TooManyVatsException, Tests\BasicTestCase}; use Exception; @@ -27,22 +32,24 @@ class VatsTest extends BasicTestCase /** * Тестирует создание коллекции ставок * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException + * @throws Exception */ public function testConstructor() { $vats = new Vats($this->generateObjects(3)); $this->assertIsCollection($vats); $this->assertEquals(3, $vats->count()); + $this->assertAtolable($vats); } /** * Тестирует выброс исключения при установке слишком большого количества ставок через конструктор * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException */ @@ -55,9 +62,9 @@ class VatsTest extends BasicTestCase /** * Тестирует добавление ставки в начало коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::prepend - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::prepend + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException */ public function testPrepend() @@ -70,25 +77,25 @@ class VatsTest extends BasicTestCase /** * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::prepend - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::prepend + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException */ public function testTooManyVatsExceptionByPrepend() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1))) + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS))) ->prepend($this->generateObjects()); } /** * Тестирует добавление ставки в конец коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::add - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::add + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException */ public function testAdd() @@ -101,25 +108,25 @@ class VatsTest extends BasicTestCase /** * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::add - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::add + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException */ public function testTooManyVatsExceptionByAdd() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1))) + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS))) ->add($this->generateObjects()); } /** * Тестирует добавление лишних ставок в конец коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::push - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::push + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException */ public function testPush() @@ -132,25 +139,25 @@ class VatsTest extends BasicTestCase /** * Тестирует выброс исключения при добавлении лишних ставок в конец коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::push - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::push + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException */ public function testTooManyVatsExceptionByPush() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1))) + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS))) ->push(...$this->generateObjects()); } /** * Тестирует добавление ставки в начало коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::merge - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::merge + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException */ public function testMerge() @@ -163,22 +170,60 @@ class VatsTest extends BasicTestCase /** * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции * - * @covers \AtolOnline\Entities\EntityCollection - * @covers \AtolOnline\Entities\EntityCollection::merge - * @covers \AtolOnline\Entities\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::merge + * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException */ public function testTooManyVatsExceptionByMerge() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(9))) + (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS - 1))) ->merge($this->generateObjects(2)); } + /** + * Тестирует выброс исключения при наличии скаляров в коллекции + * + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::checkClass + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize + * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws Exception + */ + public function testInvalidCollectionItemExceptionScalar(): void + { + $this->expectException(InvalidEntityInCollectionException::class); + $this->expectExceptionMessage("(string)'bad element'"); + (new Vats($this->generateObjects(2))) + ->merge('bad element') + ->jsonSerialize(); + } + + /** + * Тестирует выброс исключения при наличии объектов не тех классов в коллекции + * + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws Exception + */ + public function testInvalidCollectionItemExceptionObject(): void + { + $this->expectException(InvalidEntityInCollectionException::class); + $this->expectExceptionMessage(Payment::class); + (new Vats($this->generateObjects())) + ->merge([new Payment(PaymentTypes::PREPAID, 1)]) + ->jsonSerialize(); + } + /** * Генерирует массив тестовых объектов ставок НДС * + * @param int $count + * @return Vat[] * @throws InvalidEnumValueException * @throws Exception */ diff --git a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php index 5cb76f1..5ee5a65 100644 --- a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php +++ b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php @@ -19,8 +19,8 @@ use AtolOnline\{ Exceptions\InvalidInnLengthException, Exceptions\InvalidPhoneException, Exceptions\TooLongPayingAgentOperationException, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса агента @@ -32,6 +32,7 @@ class AgentInfoTest extends BasicTestCase * * @covers \AtolOnline\Entities\AgentInfo * @covers \AtolOnline\Entities\AgentInfo::jsonSerialize + * @throws Exception */ public function testConstructorWithoutArgs(): void { @@ -58,6 +59,7 @@ class AgentInfoTest extends BasicTestCase * @throws TooLongPayingAgentOperationException * @throws InvalidInnLengthException * @throws InvalidEnumValueException + * @throws Exception */ public function testConstructorWithArgs(): void { diff --git a/tests/AtolOnline/Tests/Entities/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php index b498433..a8b55ff 100644 --- a/tests/AtolOnline/Tests/Entities/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -18,6 +18,7 @@ use AtolOnline\{ Exceptions\TooLongEmailException, Helpers, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса покупателя @@ -29,6 +30,7 @@ class ClientTest extends BasicTestCase * * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::jsonSerialize + * @throws Exception */ public function testConstructorWithoutArgs(): void { @@ -48,6 +50,7 @@ class ClientTest extends BasicTestCase * @covers \AtolOnline\Entities\Client::getPhone * @covers \AtolOnline\Entities\Client::getEmail * @covers \AtolOnline\Entities\Client::getInn + * @throws Exception */ public function testConstructorWithArgs(): void { diff --git a/tests/AtolOnline/Tests/Entities/CompanyTest.php b/tests/AtolOnline/Tests/Entities/CompanyTest.php index 2ed59ef..1012cd3 100644 --- a/tests/AtolOnline/Tests/Entities/CompanyTest.php +++ b/tests/AtolOnline/Tests/Entities/CompanyTest.php @@ -20,6 +20,7 @@ use AtolOnline\{ Exceptions\TooLongPaymentAddressException, Helpers, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса продавца @@ -39,6 +40,7 @@ class CompanyTest extends BasicTestCase * @covers \AtolOnline\Entities\Company::getSno * @covers \AtolOnline\Entities\Company::getInn * @covers \AtolOnline\Entities\Company::getPaymentAddress + * @throws Exception */ public function testConstructor() { @@ -61,6 +63,12 @@ class CompanyTest extends BasicTestCase * @covers \AtolOnline\Entities\Company * @covers \AtolOnline\Entities\Company::setEmail * @covers \AtolOnline\Exceptions\TooLongEmailException + * @throws InvalidEmailException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongEmailException + * @throws TooLongPaymentAddressException */ public function testEmailTooLongException() { @@ -100,6 +108,12 @@ class CompanyTest extends BasicTestCase * @covers \AtolOnline\Entities\Company * @covers \AtolOnline\Entities\Company::setInn * @covers \AtolOnline\Exceptions\InvalidInnLengthException + * @throws InvalidEmailException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongEmailException + * @throws TooLongPaymentAddressException */ public function testInvalidInnLengthException() { @@ -113,6 +127,12 @@ class CompanyTest extends BasicTestCase * @covers \AtolOnline\Entities\Company * @covers \AtolOnline\Entities\Company::setPaymentAddress * @covers \AtolOnline\Exceptions\TooLongPaymentAddressException + * @throws InvalidEmailException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongEmailException + * @throws TooLongPaymentAddressException */ public function testTooLongPaymentAddressException() { diff --git a/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php b/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php index 30ad629..e01cde6 100644 --- a/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php +++ b/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php @@ -16,8 +16,8 @@ use AtolOnline\{ Exceptions\InvalidCorrectionDateException, Exceptions\InvalidEnumValueException, Helpers, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса данных коррекции @@ -39,6 +39,7 @@ class CorrectionInfoTest extends BasicTestCase * @throws InvalidEnumValueException * @throws InvalidCorrectionDateException * @throws EmptyCorrectionNumberException + * @throws Exception */ public function testConstructor(): void { diff --git a/tests/AtolOnline/Tests/Entities/ItemTest.php b/tests/AtolOnline/Tests/Entities/ItemTest.php index f55aa2e..35dc71e 100644 --- a/tests/AtolOnline/Tests/Entities/ItemTest.php +++ b/tests/AtolOnline/Tests/Entities/ItemTest.php @@ -41,8 +41,8 @@ use AtolOnline\{ Exceptions\TooLongUserdataException, Exceptions\TooManyException, Helpers, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класс продавца @@ -67,6 +67,7 @@ class ItemTest extends BasicTestCase * @throws NegativeItemPriceException * @throws EmptyItemNameException * @throws NegativeItemQuantityException + * @throws Exception */ public function testConstructor(): void { @@ -263,6 +264,7 @@ class ItemTest extends BasicTestCase * @throws TooHighItemPriceException * @throws NegativeItemQuantityException * @throws TooLongItemNameException + * @throws Exception */ public function testValidEnums(): void { @@ -332,11 +334,14 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::getVat * @covers \AtolOnline\Entities\Item::jsonSerialize * @throws EmptyItemNameException + * @throws InvalidEnumValueException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException * @throws TooHighItemPriceException + * @throws TooHighItemSumException * @throws TooLongItemNameException * @throws TooManyException + * @throws Exception */ public function testValidVatByString(): void { @@ -363,11 +368,14 @@ class ItemTest extends BasicTestCase * @covers \AtolOnline\Entities\Item::getVat * @covers \AtolOnline\Entities\Item::jsonSerialize * @throws EmptyItemNameException + * @throws InvalidEnumValueException * @throws NegativeItemPriceException * @throws NegativeItemQuantityException * @throws TooHighItemPriceException + * @throws TooHighItemSumException * @throws TooLongItemNameException * @throws TooManyException + * @throws Exception */ public function testValidVatByObject(): void { @@ -453,6 +461,7 @@ class ItemTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws Exception */ public function testSupplier(): void { @@ -489,6 +498,7 @@ class ItemTest extends BasicTestCase * @throws TooLongItemNameException * @throws TooLongUserdataException * @throws TooManyException + * @throws Exception */ public function testValidUserdata(): void { @@ -558,6 +568,7 @@ class ItemTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws Exception */ public function testCountryCode(): void { @@ -605,6 +616,7 @@ class ItemTest extends BasicTestCase * @throws TooLongItemNameException * @throws TooManyException * @throws InvalidDeclarationNumberException + * @throws Exception */ public function testValidDeclarationNumber(): void { @@ -675,6 +687,7 @@ class ItemTest extends BasicTestCase * @throws EmptyItemNameException * @throws NegativeItemQuantityException * @throws NegativeItemExciseException + * @throws Exception */ public function testExcise(): void { @@ -723,6 +736,7 @@ class ItemTest extends BasicTestCase * @throws TooLongItemNameException * @throws TooManyException * @throws TooLongItemCodeException + * @throws Exception */ public function testValidNomenclatureCode(): void { diff --git a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php index bf6560a..1416bfd 100644 --- a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php +++ b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php @@ -14,6 +14,7 @@ use AtolOnline\{ Exceptions\InvalidInnLengthException, Exceptions\InvalidPhoneException, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса оператора перевода @@ -46,6 +47,7 @@ class MoneyTransferOperatorTest extends BasicTestCase * @covers \AtolOnline\Entities\MoneyTransferOperator::getAddress * @throws InvalidPhoneException * @throws InvalidInnLengthException + * @throws Exception */ public function testConstructorWithArgs(): void { diff --git a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php index 0967ff0..6e194e6 100644 --- a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php +++ b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php @@ -15,6 +15,7 @@ use AtolOnline\{ Exceptions\TooLongPayingAgentOperationException, Helpers, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса платёжного агента @@ -43,6 +44,7 @@ class PayingAgentTest extends BasicTestCase * @covers \AtolOnline\Entities\PayingAgent::getPhones * @throws InvalidPhoneException * @throws TooLongPayingAgentOperationException + * @throws Exception */ public function testConstructorWithArgs(): void { diff --git a/tests/AtolOnline/Tests/Entities/PaymentTest.php b/tests/AtolOnline/Tests/Entities/PaymentTest.php index 1bc2fa7..db1368b 100644 --- a/tests/AtolOnline/Tests/Entities/PaymentTest.php +++ b/tests/AtolOnline/Tests/Entities/PaymentTest.php @@ -13,13 +13,12 @@ use AtolOnline\{ Constants\Constraints, Entities\Payment, Enums\PaymentTypes, - Tests\BasicTestCase -}; + Tests\BasicTestCase}; use AtolOnline\Exceptions\{ InvalidEnumValueException, NegativePaymentSumException, - TooHighPaymentSumException, -}; + TooHighPaymentSumException,}; +use Exception; /** * Набор тестов для проверки работы класса оплаты @@ -39,6 +38,7 @@ class PaymentTest extends BasicTestCase * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws Exception */ public function testConstructor(): void { diff --git a/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php b/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php index ffe501e..b2b38af 100644 --- a/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php @@ -13,6 +13,7 @@ use AtolOnline\{ Entities\ReceivePaymentsOperator, Exceptions\InvalidPhoneException, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса оператора по приёму платежей @@ -38,6 +39,7 @@ class ReceivePaymentsOperatorTest extends BasicTestCase * @covers \AtolOnline\Entities\ReceivePaymentsOperator::setPhones * @covers \AtolOnline\Entities\ReceivePaymentsOperator::getPhones * @throws InvalidPhoneException + * @throws Exception */ public function testConstructorWithArgs(): void { diff --git a/tests/AtolOnline/Tests/Entities/SupplierTest.php b/tests/AtolOnline/Tests/Entities/SupplierTest.php index 57fb253..b24d670 100644 --- a/tests/AtolOnline/Tests/Entities/SupplierTest.php +++ b/tests/AtolOnline/Tests/Entities/SupplierTest.php @@ -14,6 +14,7 @@ use AtolOnline\{ Exceptions\InvalidInnLengthException, Exceptions\InvalidPhoneException, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса поставщика @@ -44,6 +45,7 @@ class SupplierTest extends BasicTestCase * @covers \AtolOnline\Entities\Supplier::getInn * @throws InvalidPhoneException * @throws InvalidInnLengthException + * @throws Exception */ public function testConstructorWithArgs(): void { diff --git a/tests/AtolOnline/Tests/Entities/VatTest.php b/tests/AtolOnline/Tests/Entities/VatTest.php index d1b1ab0..40a2568 100644 --- a/tests/AtolOnline/Tests/Entities/VatTest.php +++ b/tests/AtolOnline/Tests/Entities/VatTest.php @@ -14,6 +14,7 @@ use AtolOnline\{ Enums\VatTypes, Exceptions\InvalidEnumValueException, Tests\BasicTestCase}; +use Exception; /** * Набор тестов для проверки работы класса ставки НДС @@ -70,6 +71,7 @@ class VatTest extends BasicTestCase * @covers \AtolOnline\Entities\Vat::getCalculated * @covers \AtolOnline\Entities\Vat::jsonSerialize * @throws InvalidEnumValueException + * @throws Exception */ public function testConstructor(string $type, float $sum): void { From 359264db6442ee9a11e26aeab591e0cd338a1258 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 19:21:42 +0800 Subject: [PATCH 61/85] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D0=B0=D1=8F=20=D1=81?= =?UTF-8?q?=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20`AdditionalUserPro?= =?UTF-8?q?ps`=20=D1=81=20=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=B4=D0=BB=D1=8F=20=D0=B1=D1=83=D0=B4=D1=83=D1=89?= =?UTF-8?q?=D0=B5=D0=B9=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B2=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Constraints.php | 24 +++- src/Constants/Ffd105Tags.php | 15 +++ src/Entities/AdditionalUserProps.php | 127 ++++++++++++++++++ .../EmptyAddUserPropNameException.php | 25 ++++ .../EmptyAddUserPropValueException.php | 25 ++++ .../TooLongAddUserPropNameException.php | 28 ++++ .../TooLongAddUserPropValueException.php | 28 ++++ src/TestEnvParams.php | 1 + .../Entities/AdditionalUserPropsTest.php | 104 ++++++++++++++ 9 files changed, 372 insertions(+), 5 deletions(-) create mode 100644 src/Entities/AdditionalUserProps.php create mode 100644 src/Exceptions/EmptyAddUserPropNameException.php create mode 100644 src/Exceptions/EmptyAddUserPropValueException.php create mode 100644 src/Exceptions/TooLongAddUserPropNameException.php create mode 100644 src/Exceptions/TooLongAddUserPropValueException.php create mode 100644 tests/AtolOnline/Tests/Entities/AdditionalUserPropsTest.php diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index 49dfec6..8c9dd34 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -128,6 +128,11 @@ final class Constraints */ const MAX_COUNT_DOC_VATS = 6; + /** + * Максимальная сумма одной оплаты + */ + const MAX_COUNT_PAYMENT_SUM = 99999.999; + /** * Максимальная длина имени кассира (1021) * @@ -142,16 +147,25 @@ final class Constraints */ const MAX_LENGTH_ITEM_CODE = 32; + /** + * Максимальная длина наименования дополнительного реквизита (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'; - /** - * Максимальная сумма одной оплаты - */ - const MAX_COUNT_PAYMENT_SUM = 99999.999; - /** * Регулярное выражение для валидации строки ИНН * diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index 32a015a..ed6cc5f 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -226,4 +226,19 @@ final class Ffd105Tags * Сумма НДС чека по расч. ставке 20/120 */ const DOC_VAT_TYPE_VAT120 = 1106; + + /** + * Дополнительный реквизит пользователя + */ + const DOC_ADD_USER_PROP = 1084; + + /** + * Наименование дополнительного реквизита пользователя + */ + const DOC_ADD_USER_PROP_NAME = 1085; + + /** + * Значение дополнительного реквизита пользователя + */ + const DOC_ADD_USER_PROP_VALUE = 1086; } diff --git a/src/Entities/AdditionalUserProps.php b/src/Entities/AdditionalUserProps.php new file mode 100644 index 0000000..0d1739b --- /dev/null +++ b/src/Entities/AdditionalUserProps.php @@ -0,0 +1,127 @@ +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] + public function jsonSerialize(): array + { + return [ + 'name' => $this->getName(), + 'value' => $this->getValue(), + ]; + } +} diff --git a/src/Exceptions/EmptyAddUserPropNameException.php b/src/Exceptions/EmptyAddUserPropNameException.php new file mode 100644 index 0000000..567d18b --- /dev/null +++ b/src/Exceptions/EmptyAddUserPropNameException.php @@ -0,0 +1,25 @@ +assertAtolable( + new AdditionalUserProps('name', 'value'), + [ + 'name' => 'name', + 'value' => 'value', + ] + ); + } + + /** + * Тестирует выброс исключения при слишком длинном наименовании + * + * @covers \AtolOnline\Entities\AdditionalUserProps::setName + * @covers \AtolOnline\Exceptions\TooLongAddUserPropNameException + * @throws EmptyAddUserPropNameException + * @throws EmptyAddUserPropValueException + * @throws TooLongAddUserPropValueException + */ + public function testTooLongAddCheckPropNameException(): void + { + $this->expectException(TooLongAddUserPropNameException::class); + new AdditionalUserProps(Helpers::randomStr(Constraints::MAX_LENGTH_ADD_USER_PROP_NAME + 1), 'value'); + } + + /** + * Тестирует выброс исключения при пустом наименовании + * + * @covers \AtolOnline\Entities\AdditionalUserProps::setName + * @covers \AtolOnline\Exceptions\EmptyAddUserPropNameException + */ + public function testEmptyAddCheckPropNameException(): void + { + $this->expectException(EmptyAddUserPropNameException::class); + new AdditionalUserProps('', 'value'); + } + + /** + * Тестирует выброс исключения при слишком длинном значении + * + * @covers \AtolOnline\Entities\AdditionalUserProps::setValue + * @covers \AtolOnline\Exceptions\TooLongAddUserPropValueException + * @throws EmptyAddUserPropNameException + * @throws EmptyAddUserPropValueException + * @throws TooLongAddUserPropValueException + * @throws TooLongAddUserPropNameException + */ + public function testTooLongAddCheckPropValueException(): void + { + $this->expectException(TooLongAddUserPropValueException::class); + new AdditionalUserProps('name', Helpers::randomStr(Constraints::MAX_LENGTH_ADD_USER_PROP_VALUE + 1)); + } + + /** + * Тестирует выброс исключения при пустом значении + * + * @covers \AtolOnline\Entities\AdditionalUserProps::setValue + * @covers \AtolOnline\Exceptions\EmptyAddUserPropValueException + */ + public function testEmptyAddCheckPropValueException(): void + { + $this->expectException(EmptyAddUserPropValueException::class); + new AdditionalUserProps('name', ''); + } +} From 1f3d5d2f3db5a8c2e4b53462188c720a6a588300 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 6 Dec 2021 20:07:17 +0800 Subject: [PATCH 62/85] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20`Receipt`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants/Constraints.php | 11 +- src/Constants/Ffd105Tags.php | 5 + src/Entities/AdditionalUserProps.php | 2 +- src/Entities/Receipt.php | 394 +++++++++++++++ src/Entities/todo_Document.php | 468 ------------------ .../EmptyAddUserPropNameException.php | 4 +- .../EmptyAddUserPropValueException.php | 6 +- .../TooLongAddCheckPropException.php | 28 ++ .../TooLongAddUserPropNameException.php | 4 +- .../TooLongAddUserPropValueException.php | 4 +- 10 files changed, 446 insertions(+), 480 deletions(-) create mode 100644 src/Entities/Receipt.php delete mode 100644 src/Entities/todo_Document.php create mode 100644 src/Exceptions/TooLongAddCheckPropException.php diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index 8c9dd34..ea2a905 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -148,14 +148,21 @@ final class Constraints const MAX_LENGTH_ITEM_CODE = 32; /** - * Максимальная длина наименования дополнительного реквизита (1085) + * Максимальная длина значения дополнительного реквизита чека (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) + * Максимальная длина значения дополнительного реквизита пользователя (1086) * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32 */ diff --git a/src/Constants/Ffd105Tags.php b/src/Constants/Ffd105Tags.php index ed6cc5f..1fb0914 100644 --- a/src/Constants/Ffd105Tags.php +++ b/src/Constants/Ffd105Tags.php @@ -227,6 +227,11 @@ final class Ffd105Tags */ const DOC_VAT_TYPE_VAT120 = 1106; + /** + * Значение дополнительного реквизита чека + */ + const DOC_ADD_CHECK_PROP_VALUE = 1192; + /** * Дополнительный реквизит пользователя */ diff --git a/src/Entities/AdditionalUserProps.php b/src/Entities/AdditionalUserProps.php index 0d1739b..bec5123 100644 --- a/src/Entities/AdditionalUserProps.php +++ b/src/Entities/AdditionalUserProps.php @@ -20,7 +20,7 @@ use AtolOnline\Exceptions\{ use JetBrains\PhpStorm\Pure; /** - * Класс, описывающий дополнительный реквизит + * Класс, описывающий дополнительный реквизит пользователя * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32 */ diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php new file mode 100644 index 0000000..8f1326b --- /dev/null +++ b/src/Entities/Receipt.php @@ -0,0 +1,394 @@ +setClient($client)->setCompany($company)->setItems($items)->setPayments($payments); + } + + /** + * Возвращает установленного покупателя + * + * @return Client + */ + public function getClient(): Client + { + return $this->client; + } + + /** + * Устанаваливает покупателя + * + * @param Client $client + * @return Receipt + */ + public function setClient(Client $client): self + { + $this->client = $client; + return $this; + } + + /** + * Возвращает установленного продавца + * + * @return Company + */ + public function getCompany(): Company + { + return $this->company; + } + + /** + * Устанаваливает продавца + * + * @param Company $company + * @return Receipt + */ + public function setCompany(Company $company): self + { + $this->company = $company; + return $this; + } + + /** + * Возвращает установленного агента + * + * @return AgentInfo + */ + public function getAgentInfo(): ?AgentInfo + { + return $this->agent_info; + } + + /** + * Устанаваливает агента + * + * @param AgentInfo|null $agent_info + * @return Receipt + */ + 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 Receipt + */ + public function setSupplier(?Supplier $supplier): self + { + $this->supplier = $supplier; + return $this; + } + + /** + * Возвращает установленную коллекцию предметов расчёта + * + * @return Items + */ + public function getItems(): Items + { + return $this->items; + } + + /** + * Устанаваливает коллекцию предметов расчёта + * + * @todo исключение при пустой коллекции + * @param Items $items + * @return Receipt + */ + public function setItems(Items $items): self + { + $this->items = $items; + return $this; + } + + /** + * Возвращает установленную коллекцию оплат + * + * @return Payments + */ + public function getPayments(): Payments + { + return $this->payments; + } + + /** + * Устанаваливает коллекцию оплат + * + * @todo исключение при пустой коллекции + * @param Payments $payments + * @return Receipt + */ + public function setPayments(Payments $payments): self + { + $this->payments = $payments; + return $this; + } + + /** + * Возвращает установленную коллекцию ставок НДС + * + * @return Vats|null + */ + public function getVats(): ?Vats + { + return $this->vats; + } + + /** + * Устанаваливает коллекцию ставок НДС + * + * @param Vats|null $vats + * @return Receipt + */ + public function setVats(?Vats $vats): self + { + $this->vats = $vats; + return $this; + } + + /** + * Возвращает полную сумму чека + * + * @return float + */ + public function getTotal(): float + { + return $this->total; + } + + /** + * Устанавливает полную сумму чека + * + * @param float $total + * @return Receipt + */ + public function setTotal(float $total): self + { + $this->total = $total; + return $this; + } + + /** + * Возвращает установленного кассира + * + * @return string|null + */ + public function getCashier(): ?string + { + return $this->cashier; + } + + /** + * Устанаваливает кассира + * + * @param string|null $cashier + * @return Receipt + * @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 = empty($cashier) ? null : $cashier; + return $this; + } + + /** + * Возвращает установленный дополнительный реквизит чека + * + * @return string|null + */ + public function getAddCheckProps(): ?string + { + return $this->add_check_props; + } + + /** + * Устанаваливает дополнительный реквизит чека + * + * @param string|null $add_check_props + * @return Receipt + * @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 = empty($add_check_props) ? null : $add_check_props; + return $this; + } + + /** + * Возвращает установленный дополнительный реквизит пользователя + * + * @return AdditionalUserProps|null + */ + public function getAddUserProps(): ?AdditionalUserProps + { + return $this->add_user_props; + } + + /** + * Устанаваливает дополнительный реквизит пользователя + * + * @param AdditionalUserProps|null $add_user_props + * @return Receipt + */ + public function setAddUserProps(?AdditionalUserProps $add_user_props): self + { + $this->add_user_props = $add_user_props; + return $this; + } + + /** + * Возвращает массив для кодирования в json + * + * @throws Exception + */ + public function jsonSerialize(): array + { + $json = [ + 'client' => $this->getClient(), + 'company' => $this->getCompany(), + 'items' => $this->getItems(), + 'total' => $this->getTotal(), + 'payment' => $this->getPayments(), + ]; + $this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo(); + $this->getSupplier()?->jsonSerialize() && $json['vats'] = $this->getVats(); + $this->getVats()?->jsonSerialize() && $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(); + return $json; + } + + ///** + // * Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020. + // * + // * @return float + // * @throws Exception + // */ + //public function calcTotal(): float + //{ + // $sum = 0; + // $this->clearVats(); + // foreach ($this->items->get() as $item) { + // $sum += $item->calcSum(); + // $this->addVat(new Vat($item->getVat()->getType(), $item->getSum())); + // } + // return $this->total = round($sum, 2); + //} +} diff --git a/src/Entities/todo_Document.php b/src/Entities/todo_Document.php deleted file mode 100644 index f79bde2..0000000 --- a/src/Entities/todo_Document.php +++ /dev/null @@ -1,468 +0,0 @@ -vats = new VatArray(); - $this->payments = new PaymentArray(); - $this->items = new ItemArray(); - } - - /** - * Удаляет все налоги из документа и предметов расчёта - * - * @return $this - * @throws TooManyVatsException Слишком много ставок НДС - */ - public function clearVats(): todoDocument - { - $this->setVats([]); - return $this; - } - - /** - * Добавляет новую ставку НДС в массив ставок НДС - * - * @param Vat $vat Объект ставки НДС - * @return $this - * @throws TooManyVatsException Слишком много ставок НДС - */ - public function addVat(Vat $vat): todoDocument - { - $this->vats->add($vat); - return $this; - } - - /** - * Возвращает массив ставок НДС - * - * @return Vat[] - */ - public function getVats(): array - { - return $this->vats->get(); - } - - /** - * Устанавливает массив ставок НДС - * - * @param Vat[] $vats Массив ставок НДС - * @return $this - * @throws TooManyVatsException Слишком много ставок НДС - * @throws Exception - */ - public function setVats(array $vats): todoDocument - { - $this->vats->set($vats); - return $this; - } - - /** - * Добавляет новую оплату в массив оплат - * - * @param todoPayment $payment Объект оплаты - * @return $this - * @throws Exception - * @throws TooManyPaymentsException Слишком много оплат - */ - public function addPayment(todoPayment $payment): todoDocument - { - if (count($this->getPayments()) == 0 && !$payment->getSum()) { - $payment->setSum($this->calcTotal()); - } - $this->payments->add($payment); - return $this; - } - - /** - * Возвращает массив оплат - * - * @return todoPayment[] - */ - public function getPayments(): array - { - return $this->payments->get(); - } - - /** - * Устанавливает массив оплат - * - * @param todoPayment[] $payments Массив оплат - * @return $this - * @throws TooManyPaymentsException Слишком много оплат - */ - public function setPayments(array $payments): todoDocument - { - $this->payments->set($payments); - return $this; - } - - /** - * Добавляет новый предмет расчёта в массив предметов расчёта - * - * @param Item $item Объект предмета расчёта - * @return $this - * @throws TooManyItemsException Слишком много предметов расчёта - */ - public function addItem(Item $item): todoDocument - { - $this->items->add($item); - return $this; - } - - /** - * Возвращает массив предметов расчёта - * - * @return Item[] - */ - public function getItems(): array - { - return $this->items->get(); - } - - /** - * Устанавливает массив предметов расчёта - * - * @param Item[] $items Массив предметов расчёта - * @return $this - * @throws TooManyItemsException Слишком много предметов расчёта - */ - public function setItems(array $items): todoDocument - { - $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): todoDocument - { - $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): todoDocument - { - $this->company = $company; - return $this; - } - - /** - * Возвращает ФИО кассира. Тег ФФД - 1021. - * - * @return string|null - */ - public function getCashier(): ?string - { - return $this->cashier; - } - - /** - * Устанавливает ФИО кассира. Тег ФФД - 1021. - * - * @param string|null $cashier - * @return $this - * @throws TooLongCashierException - */ - public function setCashier(?string $cashier): todoDocument - { - if ($cashier !== null) { - $cashier = trim($cashier); - if (mb_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) { - throw new TooLongCashierException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME); - } - } - $this->cashier = $cashier; - return $this; - } - - /** - * Возвращает данные коррекции - * - * @return todoCorrectionInfo|null - */ - public function getCorrectionInfo(): ?todoCorrectionInfo - { - return $this->correction_info; - } - - /** - * Устанавливает данные коррекции - * - * @param todoCorrectionInfo|null $correction_info - * @return $this - */ - public function setCorrectionInfo(?todoCorrectionInfo $correction_info): todoDocument - { - $this->correction_info = $correction_info; - return $this; - } - - /** - * Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020. - * - * @return float - * @throws Exception - */ - public function calcTotal(): float - { - $sum = 0; - $this->clearVats(); - foreach ($this->items->get() as $item) { - $sum += $item->calcSum(); - $this->addVat(new Vat($item->getVat()->getType(), $item->getSum())); - } - return $this->total = round($sum, 2); - } - - /** - * Возвращает итоговую сумму чека. Тег ФФД - 1020. - * - * @return float - */ - public function getTotal(): float - { - return $this->total; - } - - /** - * Собирает объект документа из сырой json-строки - * - * @param string $json - * @return todoDocument - * @throws TooLongEmailException - * @throws InvalidEmailException - * @throws AtolException - * @throws InvalidInnLengthException - * @throws InvalidJsonException - * @throws TooLongItemNameException - * @throws TooLongPaymentAddressException - * @throws TooLongClientContactException - * @throws TooHighPriceException - * @throws TooManyException - * @throws TooManyItemsException - * @throws TooManyPaymentsException - * @throws TooLongMeasurementUnitException - * @throws TooLongUserdataException - * @throws Exception - */ - public static function fromRaw(string $json): todoDocument - { - $array = json_decode($json, true); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new InvalidJsonException(); - } - $doc = new self(); - if (isset($array['company'])) { - $doc->setCompany(new Company( - $array['company']['sno'] ?? null, - $array['company']['inn'] ?? null, - $array['company']['payment_address'] ?? null, - $array['company']['email'] ?? null - )); - } - if (isset($array['client'])) { - $doc->setClient(new Client( - $array['client']['name'] ?? null, - $array['client']['phone'] ?? null, - $array['client']['email'] ?? null, - $array['client']['inn'] ?? null - )); - } - if (isset($array['correction_info'])) { - $doc->setCorrectionInfo(new todoCorrectionInfo( - $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 todoPayment(); - if (isset($ar_payment['type'])) { - $payment->setType($ar_payment['type']); - } - if (isset($ar_payment['sum'])) { - $payment->setSum($ar_payment['sum']); - } - $doc->payments->add($payment); - } - } - if (isset($array['vats'])) { - foreach ($array['vats'] as $vat_payment) { - $vat = new Vat(); - if (isset($vat_payment['type'])) { - $vat->setType($vat_payment['type']); - } - if (isset($vat_payment['sum'])) { - $vat->setSum($vat_payment['sum']); - } - $doc->vats->add($vat); - } - } - if (isset($array['total']) && $array['total'] != $doc->calcTotal()) { - throw new AtolException('Real total sum not equals to provided in JSON one'); - } - return $doc; - } - - /** - * Возвращает массив для кодирования в json - * - * @throws Exception - */ - public function jsonSerialize(): array - { - if ($this->getCompany()) { - $json['company'] = $this->getCompany()->jsonSerialize(); // обязательно - } - if ($this->getPayments()) { - $json['payments'] = $this->payments->jsonSerialize(); // обязательно - } - if ($this->getCashier()) { - $json['cashier'] = $this->getCashier(); - } - if ($this->getCorrectionInfo()) { - $json['correction_info'] = $this->getCorrectionInfo()->jsonSerialize(); // обязательно для коррекционных - } else { - if ($this->getClient()) { - $json['client'] = $this->getClient()->jsonSerialize(); // обязательно для некоррекционных - } - if ($this->getItems()) { - $json['items'] = $this->items->jsonSerialize(); // обязательно для некоррекционных - } - $json['total'] = $this->calcTotal(); // обязательно для некоррекционных - } - if ($this->getVats()) { - $json['vats'] = $this->vats->jsonSerialize(); - } - return $json; - } -} diff --git a/src/Exceptions/EmptyAddUserPropNameException.php b/src/Exceptions/EmptyAddUserPropNameException.php index 567d18b..07f5d95 100644 --- a/src/Exceptions/EmptyAddUserPropNameException.php +++ b/src/Exceptions/EmptyAddUserPropNameException.php @@ -14,12 +14,12 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Ffd105Tags; /** - * Исключение, возникающее при пустом наименовании дополнительного реквизита + * Исключение, возникающее при пустом наименовании дополнительного реквизита пользователя * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32 */ class EmptyAddUserPropNameException extends AtolException { - protected $message = 'Наименование дополнительного реквизита не может быть пустым'; + protected $message = 'Наименование дополнительного реквизита пользователя не может быть пустым'; protected array $ffd_tags = [Ffd105Tags::DOC_ADD_USER_PROP_NAME]; } diff --git a/src/Exceptions/EmptyAddUserPropValueException.php b/src/Exceptions/EmptyAddUserPropValueException.php index d518bd6..4f7d8c2 100644 --- a/src/Exceptions/EmptyAddUserPropValueException.php +++ b/src/Exceptions/EmptyAddUserPropValueException.php @@ -14,12 +14,12 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Ffd105Tags; /** - * Исключение, возникающее при пустом наименовании дополнительного реквизита + * Исключение, возникающее при пустом наименовании дополнительного реквизита пользователя * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 32 */ class EmptyAddUserPropValueException extends AtolException { - protected $message = 'Значение дополнительного реквизита не может быть пустым'; - protected array $ffd_tags = [Ffd105Tags::DOC_ADD_USER_PROP_NAME]; + protected $message = 'Значение дополнительного реквизита пользователя не может быть пустым'; + protected array $ffd_tags = [Ffd105Tags::DOC_ADD_USER_PROP_VALUE]; } diff --git a/src/Exceptions/TooLongAddCheckPropException.php b/src/Exceptions/TooLongAddCheckPropException.php new file mode 100644 index 0000000..1803b5e --- /dev/null +++ b/src/Exceptions/TooLongAddCheckPropException.php @@ -0,0 +1,28 @@ + Date: Tue, 7 Dec 2021 20:04:03 +0800 Subject: [PATCH 63/85] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=BE=D0=B9=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BF=D0=BE=20=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `BasicTestCase::assertAtolable() => assertIsAtolable()` - генерация тестовых объектов `Vat`, `Payment` и `Item` вынесены в `BasicTestCase` --- src/Api/KktMonitor.php | 1 - src/Collections/EntityCollection.php | 24 +++-- tests/AtolOnline/Tests/Api/KktMonitorTest.php | 2 +- tests/AtolOnline/Tests/BasicTestCase.php | 98 ++++++++++++++++++- .../Tests/Collections/ItemsTest.php | 51 +++------- .../Tests/Collections/PaymentsTest.php | 50 +++------- .../AtolOnline/Tests/Collections/VatsTest.php | 76 ++++++-------- .../Entities/AdditionalUserPropsTest.php | 2 +- .../Tests/Entities/AgentInfoTest.php | 16 +-- .../AtolOnline/Tests/Entities/ClientTest.php | 12 +-- .../AtolOnline/Tests/Entities/CompanyTest.php | 2 +- .../Tests/Entities/CorrectionInfoTest.php | 2 +- tests/AtolOnline/Tests/Entities/ItemTest.php | 20 ++-- .../Tests/Entities/KktEntityTest.php | 2 +- .../Entities/MoneyTransferOperatorTest.php | 10 +- .../Tests/Entities/PayingAgentTest.php | 6 +- .../AtolOnline/Tests/Entities/PaymentTest.php | 2 +- .../Entities/ReceivePaymentsOperatorTest.php | 2 +- .../Tests/Entities/SupplierTest.php | 8 +- tests/AtolOnline/Tests/Entities/VatTest.php | 2 +- 20 files changed, 220 insertions(+), 168 deletions(-) diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index 1e63643..e43f43c 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -100,7 +100,6 @@ class KktMonitor extends AtolClient /** * Возвращает информацию о конкретной ККТ по её серийному номеру * - * @todo кастовать к отдельному классу со своими геттерами * @param string $serial_number * @return Kkt * @throws GuzzleException diff --git a/src/Collections/EntityCollection.php b/src/Collections/EntityCollection.php index 74e4c77..fc80b97 100644 --- a/src/Collections/EntityCollection.php +++ b/src/Collections/EntityCollection.php @@ -22,10 +22,12 @@ abstract class EntityCollection extends Collection { /** * @inheritDoc + * @throws InvalidEntityInCollectionException */ public function __construct($items = []) { $this->checkCount($items); + $this->checkItemsClasses($items); parent::__construct($items); } @@ -71,19 +73,17 @@ abstract class EntityCollection extends Collection */ public function jsonSerialize(): array { - $this->each(function ($item) { - $this->checkClass($item); - }); + $this->checkItemsClasses(); return parent::jsonSerialize(); } /** - * Проверяет количество ставок + * Проверяет количество элементов коллекции * * @param array $items Массив элементов, если пустой - проверит содержимое коллекции * @return void */ - private function checkCount(array $items = []): void + public function checkCount(array $items = []): void { if (count($items) > static::MAX_COUNT || $this->count() === static::MAX_COUNT) { throw new (static::EXCEPTION_CLASS)(static::MAX_COUNT); @@ -91,14 +91,24 @@ abstract class EntityCollection extends Collection } /** - * Проверяет корректность класса объекта + * Проверяет корректность класса элемента коллекции * * @throws InvalidEntityInCollectionException */ - private function checkClass(mixed $item): void + public function checkItemClass(mixed $item): void { if (!is_object($item) || $item::class !== static::ENTITY_CLASS) { throw new InvalidEntityInCollectionException(static::class, static::ENTITY_CLASS, $item); } } + + /** + * Проверяет корректность классов элементов коллекции + * + * @throws InvalidEntityInCollectionException + */ + public function checkItemsClasses(array $items = []): void + { + (empty($items) ? $this : collect($items))->each(fn ($item) => $this->checkItemClass($item)); + } } diff --git a/tests/AtolOnline/Tests/Api/KktMonitorTest.php b/tests/AtolOnline/Tests/Api/KktMonitorTest.php index 502432b..34ccb7f 100644 --- a/tests/AtolOnline/Tests/Api/KktMonitorTest.php +++ b/tests/AtolOnline/Tests/Api/KktMonitorTest.php @@ -339,7 +339,7 @@ class KktMonitorTest extends BasicTestCase $kkt = $client->getOne($serial_number); $this->assertNotEmpty($client->getResponse()); $this->assertIsSameClass(Kkt::class, $kkt); - $this->assertAtolable($kkt); + $this->assertIsAtolable($kkt); $this->assertNotNull($kkt->serialNumber); $this->assertEquals($serial_number, $kkt->serialNumber); } diff --git a/tests/AtolOnline/Tests/BasicTestCase.php b/tests/AtolOnline/Tests/BasicTestCase.php index 9a32546..8c6aacf 100644 --- a/tests/AtolOnline/Tests/BasicTestCase.php +++ b/tests/AtolOnline/Tests/BasicTestCase.php @@ -13,6 +13,20 @@ namespace AtolOnline\Tests; use AtolOnline\Collections\EntityCollection; use AtolOnline\Entities\Entity; +use AtolOnline\Entities\Item; +use AtolOnline\Entities\Payment; +use AtolOnline\Entities\Vat; +use AtolOnline\Enums\PaymentTypes; +use AtolOnline\Enums\VatTypes; +use AtolOnline\Exceptions\EmptyItemNameException; +use AtolOnline\Exceptions\InvalidEnumValueException; +use AtolOnline\Exceptions\NegativeItemPriceException; +use AtolOnline\Exceptions\NegativeItemQuantityException; +use AtolOnline\Exceptions\NegativePaymentSumException; +use AtolOnline\Exceptions\TooHighItemPriceException; +use AtolOnline\Exceptions\TooHighPaymentSumException; +use AtolOnline\Exceptions\TooLongItemNameException; +use AtolOnline\Exceptions\TooManyException; use AtolOnline\Helpers; use Exception; use GuzzleHttp\Client; @@ -25,6 +39,10 @@ use PHPUnit\Framework\TestCase; */ class BasicTestCase extends TestCase { + //------------------------------------------------------------------------------------------------------------------ + // Методы для управления тестами, использующими тестовый АТОЛ API + //------------------------------------------------------------------------------------------------------------------ + /** * Проверяет наличие подключения к ресурсу по URL * @@ -65,6 +83,10 @@ class BasicTestCase extends TestCase } } + //------------------------------------------------------------------------------------------------------------------ + // Дополнительные ассерты + //------------------------------------------------------------------------------------------------------------------ + /** * Тестирует является ли объект приводимым к json-строке согласно схеме АТОЛ Онлайн * @@ -75,7 +97,7 @@ class BasicTestCase extends TestCase * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @throws Exception */ - public function assertAtolable(Entity|EntityCollection $entity, ?array $json_structure = null): void + public function assertIsAtolable(Entity|EntityCollection $entity, ?array $json_structure = null): void { $this->assertIsArray($entity->jsonSerialize()); $this->assertIsString((string)$entity); @@ -85,6 +107,8 @@ class BasicTestCase extends TestCase } } + //------------------------------------------------------------------------------------------------------------------ + // Ассерты проверки наследования //------------------------------------------------------------------------------------------------------------------ /** @@ -203,6 +227,8 @@ class BasicTestCase extends TestCase ); } + //------------------------------------------------------------------------------------------------------------------ + // Провайдеры данных для прогона тестов //------------------------------------------------------------------------------------------------------------------ /** @@ -288,4 +314,74 @@ class BasicTestCase extends TestCase ['abc.def@mail#archive.com'], ]; } + + //------------------------------------------------------------------------------------------------------------------ + // Генераторы тестовых объектов + //------------------------------------------------------------------------------------------------------------------ + + /** + * Генерирует массив тестовых объектов предметов расчёта + * + * @param int $count + * @return Item[] + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws Exception + */ + protected function generateItemObjects(int $count = 1): array + { + $result = []; + for ($i = 0; $i < abs($count); ++$i) { + $result[] = new Item(Helpers::randomStr(), random_int(1, 100), random_int(1, 10)); + } + return $result; + } + + /** + * Генерирует массив тестовых объектов оплаты + * + * @param int $count + * @return Payment[] + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws Exception + */ + protected function generatePaymentObjects(int $count = 1): array + { + $types = PaymentTypes::toArray(); + $result = []; + for ($i = 0; $i < abs($count); ++$i) { + $result[] = new Payment( + array_values($types)[random_int(min($types), max($types))], + random_int(1, 100) * 2 / 3 + ); + } + return $result; + } + + /** + * Генерирует массив тестовых объектов ставок НДС + * + * @param int $count + * @return Vat[] + * @throws InvalidEnumValueException + * @throws Exception + */ + protected function generateVatObjects(int $count = 1): array + { + $types = VatTypes::toArray(); + $result = []; + for ($i = 0; $i < abs($count); ++$i) { + $result[] = new Vat( + array_values($types)[random_int(0, count($types) - 1)], + random_int(1, 100) * 2 / 3 + ); + } + return $result; + } } diff --git a/tests/AtolOnline/Tests/Collections/ItemsTest.php b/tests/AtolOnline/Tests/Collections/ItemsTest.php index 7289f6e..901001b 100644 --- a/tests/AtolOnline/Tests/Collections/ItemsTest.php +++ b/tests/AtolOnline/Tests/Collections/ItemsTest.php @@ -12,18 +12,16 @@ namespace AtolOnline\Tests\Collections; use AtolOnline\{ Collections\Items, Constants\Constraints, - Entities\Item, - Helpers, Tests\BasicTestCase}; use AtolOnline\Exceptions\{ EmptyItemNameException, + InvalidEntityInCollectionException, NegativeItemPriceException, NegativeItemQuantityException, TooHighItemPriceException, TooLongItemNameException, TooManyException, - TooManyItemsException,}; -use Exception; + TooManyItemsException}; /** * Набор тестов для проверки работы класса коллекции предметов расчёта @@ -42,11 +40,12 @@ class ItemsTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws InvalidEntityInCollectionException */ public function testTooManyItemsExceptionByConstructor() { $this->expectException(TooManyItemsException::class); - new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS + 1)); + new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS + 1)); } /** @@ -61,12 +60,13 @@ class ItemsTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws InvalidEntityInCollectionException */ public function testTooManyItemsExceptionByPrepend() { $this->expectException(TooManyItemsException::class); - (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->prepend($this->generateObjects()); + (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->prepend($this->generateItemObjects()); } /** @@ -82,12 +82,13 @@ class ItemsTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws InvalidEntityInCollectionException */ public function testTooManyItemsExceptionByAdd() { $this->expectException(TooManyItemsException::class); - (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->add($this->generateObjects()); + (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->add($this->generateItemObjects()); } /** @@ -103,12 +104,13 @@ class ItemsTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws InvalidEntityInCollectionException */ public function testTooManyItemsExceptionByPush() { $this->expectException(TooManyItemsException::class); - (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->push(...$this->generateObjects()); + (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->push(...$this->generateItemObjects()); } /** @@ -124,33 +126,12 @@ class ItemsTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooLongItemNameException * @throws TooManyException + * @throws InvalidEntityInCollectionException */ public function testTooManyItemsExceptionByMerge() { $this->expectException(TooManyItemsException::class); - (new Items($this->generateObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->merge($this->generateObjects(2)); - } - - /** - * Генерирует массив тестовых объектов предметов расчёта - * - * @param int $count - * @return Item[] - * @throws EmptyItemNameException - * @throws NegativeItemPriceException - * @throws NegativeItemQuantityException - * @throws TooHighItemPriceException - * @throws TooLongItemNameException - * @throws TooManyException - * @throws Exception - */ - protected function generateObjects(int $count = 1): array - { - $result = []; - for ($i = 0; $i < abs($count); ++$i) { - $result[] = new Item(Helpers::randomStr(), random_int(1, 100), random_int(1, 10)); - } - return $result; + (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) + ->merge($this->generateItemObjects(2)); } } diff --git a/tests/AtolOnline/Tests/Collections/PaymentsTest.php b/tests/AtolOnline/Tests/Collections/PaymentsTest.php index a10d679..94882ae 100644 --- a/tests/AtolOnline/Tests/Collections/PaymentsTest.php +++ b/tests/AtolOnline/Tests/Collections/PaymentsTest.php @@ -12,14 +12,12 @@ namespace AtolOnline\Tests\Collections; use AtolOnline\{ Collections\Payments, Constants\Constraints, - Entities\Payment, - Enums\PaymentTypes, + Exceptions\InvalidEntityInCollectionException, Exceptions\InvalidEnumValueException, Exceptions\NegativePaymentSumException, Exceptions\TooHighPaymentSumException, Exceptions\TooManyPaymentsException, Tests\BasicTestCase}; -use Exception; /** * Набор тестов для проверки работы класса коллекции оплат @@ -35,11 +33,12 @@ class PaymentsTest extends BasicTestCase * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws InvalidEntityInCollectionException */ public function testTooManyPaymentsExceptionByConstructor() { $this->expectException(TooManyPaymentsException::class); - new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1)); + new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1)); } /** @@ -51,12 +50,13 @@ class PaymentsTest extends BasicTestCase * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws InvalidEntityInCollectionException */ public function testTooManyPaymentsExceptionByPrepend() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) - ->prepend($this->generateObjects()); + (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) + ->prepend($this->generatePaymentObjects()); } /** @@ -69,12 +69,13 @@ class PaymentsTest extends BasicTestCase * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws InvalidEntityInCollectionException */ public function testTooManyPaymentsExceptionByAdd() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) - ->add($this->generateObjects()); + (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) + ->add($this->generatePaymentObjects()); } /** @@ -87,12 +88,13 @@ class PaymentsTest extends BasicTestCase * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws InvalidEntityInCollectionException */ public function testTooManyPaymentsExceptionByPush() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1))) - ->push(...$this->generateObjects()); + (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1))) + ->push(...$this->generatePaymentObjects()); } /** @@ -105,34 +107,12 @@ class PaymentsTest extends BasicTestCase * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException + * @throws InvalidEntityInCollectionException */ public function testTooManyPaymentsExceptionByMerge() { $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generateObjects(Constraints::MAX_COUNT_DOC_PAYMENTS - 1))) - ->merge($this->generateObjects(2)); - } - - /** - * Генерирует массив тестовых объектов оплаты - * - * @param int $count - * @return Payment[] - * @throws InvalidEnumValueException - * @throws NegativePaymentSumException - * @throws TooHighPaymentSumException - * @throws Exception - */ - protected function generateObjects(int $count = 1): array - { - $types = PaymentTypes::toArray(); - $result = []; - for ($i = 0; $i < abs($count); ++$i) { - $result[] = new Payment( - array_values($types)[random_int(min($types), max($types))], - random_int(1, 100) * 2 / 3 - ); - } - return $result; + (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS - 1))) + ->merge($this->generatePaymentObjects(2)); } } diff --git a/tests/AtolOnline/Tests/Collections/VatsTest.php b/tests/AtolOnline/Tests/Collections/VatsTest.php index 288fe61..7a20a50 100644 --- a/tests/AtolOnline/Tests/Collections/VatsTest.php +++ b/tests/AtolOnline/Tests/Collections/VatsTest.php @@ -13,9 +13,7 @@ use AtolOnline\{ Collections\Vats, Constants\Constraints, Entities\Payment, - Entities\Vat, Enums\PaymentTypes, - Enums\VatTypes, Exceptions\InvalidEntityInCollectionException, Exceptions\InvalidEnumValueException, Exceptions\NegativePaymentSumException, @@ -39,10 +37,10 @@ class VatsTest extends BasicTestCase */ public function testConstructor() { - $vats = new Vats($this->generateObjects(3)); + $vats = new Vats($this->generateVatObjects(3)); $this->assertIsCollection($vats); $this->assertEquals(3, $vats->count()); - $this->assertAtolable($vats); + $this->assertIsAtolable($vats); } /** @@ -52,11 +50,12 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testTooManyVatsExceptionByConstructor() { $this->expectException(TooManyVatsException::class); - new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS + 1)); + new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS + 1)); } /** @@ -66,11 +65,12 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::prepend * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testPrepend() { - $vats = (new Vats($this->generateObjects(3))) - ->prepend($this->generateObjects()); + $vats = (new Vats($this->generateVatObjects(3))) + ->prepend($this->generateVatObjects()); $this->assertEquals(4, $vats->count()); } @@ -82,12 +82,13 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testTooManyVatsExceptionByPrepend() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS))) - ->prepend($this->generateObjects()); + (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS))) + ->prepend($this->generateVatObjects()); } /** @@ -97,11 +98,12 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::add * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testAdd() { - $vats = (new Vats($this->generateObjects(3))) - ->add($this->generateObjects()); + $vats = (new Vats($this->generateVatObjects(3))) + ->add($this->generateVatObjects()); $this->assertEquals(4, $vats->count()); } @@ -113,12 +115,13 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testTooManyVatsExceptionByAdd() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS))) - ->add($this->generateObjects()); + (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS))) + ->add($this->generateVatObjects()); } /** @@ -128,11 +131,12 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::push * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testPush() { - $vats = (new Vats($this->generateObjects(3))) - ->push(...$this->generateObjects(3)); + $vats = (new Vats($this->generateVatObjects(3))) + ->push(...$this->generateVatObjects(3)); $this->assertEquals(6, $vats->count()); } @@ -144,12 +148,13 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testTooManyVatsExceptionByPush() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS))) - ->push(...$this->generateObjects()); + (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS))) + ->push(...$this->generateVatObjects()); } /** @@ -159,11 +164,12 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::merge * @covers \AtolOnline\Collections\EntityCollection::checkCount * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testMerge() { - $vats = (new Vats($this->generateObjects(3))) - ->merge($this->generateObjects(3)); + $vats = (new Vats($this->generateVatObjects(3))) + ->merge($this->generateVatObjects(3)); $this->assertEquals(6, $vats->count()); } @@ -175,19 +181,20 @@ class VatsTest extends BasicTestCase * @covers \AtolOnline\Collections\EntityCollection::checkCount * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException + * @throws InvalidEntityInCollectionException */ public function testTooManyVatsExceptionByMerge() { $this->expectException(TooManyVatsException::class); - (new Vats($this->generateObjects(Constraints::MAX_COUNT_DOC_VATS - 1))) - ->merge($this->generateObjects(2)); + (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS - 1))) + ->merge($this->generateVatObjects(2)); } /** * Тестирует выброс исключения при наличии скаляров в коллекции * * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::checkClass + * @covers \AtolOnline\Collections\EntityCollection::checkItemClass * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException * @throws InvalidEnumValueException @@ -197,7 +204,7 @@ class VatsTest extends BasicTestCase { $this->expectException(InvalidEntityInCollectionException::class); $this->expectExceptionMessage("(string)'bad element'"); - (new Vats($this->generateObjects(2))) + (new Vats($this->generateVatObjects(2))) ->merge('bad element') ->jsonSerialize(); } @@ -214,29 +221,8 @@ class VatsTest extends BasicTestCase { $this->expectException(InvalidEntityInCollectionException::class); $this->expectExceptionMessage(Payment::class); - (new Vats($this->generateObjects())) + (new Vats($this->generateVatObjects())) ->merge([new Payment(PaymentTypes::PREPAID, 1)]) ->jsonSerialize(); } - - /** - * Генерирует массив тестовых объектов ставок НДС - * - * @param int $count - * @return Vat[] - * @throws InvalidEnumValueException - * @throws Exception - */ - protected function generateObjects(int $count = 1): array - { - $types = VatTypes::toArray(); - $result = []; - for ($i = 0; $i < abs($count); ++$i) { - $result[] = new Vat( - array_values($types)[random_int(0, count($types) - 1)], - random_int(1, 100) * 2 / 3 - ); - } - return $result; - } } diff --git a/tests/AtolOnline/Tests/Entities/AdditionalUserPropsTest.php b/tests/AtolOnline/Tests/Entities/AdditionalUserPropsTest.php index eecae47..8ee0f58 100644 --- a/tests/AtolOnline/Tests/Entities/AdditionalUserPropsTest.php +++ b/tests/AtolOnline/Tests/Entities/AdditionalUserPropsTest.php @@ -38,7 +38,7 @@ class AdditionalUserPropsTest extends BasicTestCase */ public function testConstructor(): void { - $this->assertAtolable( + $this->assertIsAtolable( new AdditionalUserProps('name', 'value'), [ 'name' => 'name', diff --git a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php index 5ee5a65..745890e 100644 --- a/tests/AtolOnline/Tests/Entities/AgentInfoTest.php +++ b/tests/AtolOnline/Tests/Entities/AgentInfoTest.php @@ -36,7 +36,7 @@ class AgentInfoTest extends BasicTestCase */ public function testConstructorWithoutArgs(): void { - $this->assertAtolable(new AgentInfo(), []); + $this->assertIsAtolable(new AgentInfo(), []); } /** @@ -63,20 +63,20 @@ class AgentInfoTest extends BasicTestCase */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new AgentInfo(null), []); - $this->assertAtolable(new AgentInfo(AgentTypes::ANOTHER), ['type' => AgentTypes::ANOTHER]); - $this->assertAtolable(new AgentInfo(pagent: new PayingAgent()), []); - $this->assertAtolable(new AgentInfo(mt_operator: new MoneyTransferOperator()), []); - $this->assertAtolable(new AgentInfo(rp_operator: new ReceivePaymentsOperator()), []); + $this->assertIsAtolable(new AgentInfo(null), []); + $this->assertIsAtolable(new AgentInfo(AgentTypes::ANOTHER), ['type' => AgentTypes::ANOTHER]); + $this->assertIsAtolable(new AgentInfo(pagent: new PayingAgent()), []); + $this->assertIsAtolable(new AgentInfo(mt_operator: new MoneyTransferOperator()), []); + $this->assertIsAtolable(new AgentInfo(rp_operator: new ReceivePaymentsOperator()), []); - $this->assertAtolable(new AgentInfo( + $this->assertIsAtolable(new AgentInfo( AgentTypes::ANOTHER, new PayingAgent(), new ReceivePaymentsOperator(), new MoneyTransferOperator(), ), ['type' => AgentTypes::ANOTHER]); - $this->assertAtolable(new AgentInfo( + $this->assertIsAtolable(new AgentInfo( AgentTypes::ANOTHER, new PayingAgent('test', ['+79518888888']), new ReceivePaymentsOperator(['+79519999999']), diff --git a/tests/AtolOnline/Tests/Entities/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php index a8b55ff..25c8976 100644 --- a/tests/AtolOnline/Tests/Entities/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -34,7 +34,7 @@ class ClientTest extends BasicTestCase */ public function testConstructorWithoutArgs(): void { - $this->assertAtolable(new Client(), []); + $this->assertIsAtolable(new Client(), []); } /** @@ -54,11 +54,11 @@ class ClientTest extends BasicTestCase */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new Client('John Doe'), ['name' => 'John Doe']); - $this->assertAtolable(new Client(email: 'john@example.com'), ['email' => 'john@example.com']); - $this->assertAtolable(new Client(phone: '+1/22/99*73s dsdas654 5s6'), ['phone' => '+122997365456']); - $this->assertAtolable(new Client(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); - $this->assertAtolable(new Client( + $this->assertIsAtolable(new Client('John Doe'), ['name' => 'John Doe']); + $this->assertIsAtolable(new Client(email: 'john@example.com'), ['email' => 'john@example.com']); + $this->assertIsAtolable(new Client(phone: '+1/22/99*73s dsdas654 5s6'), ['phone' => '+122997365456']); + $this->assertIsAtolable(new Client(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); + $this->assertIsAtolable(new Client( 'John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', // +122997365456 diff --git a/tests/AtolOnline/Tests/Entities/CompanyTest.php b/tests/AtolOnline/Tests/Entities/CompanyTest.php index 1012cd3..862c68e 100644 --- a/tests/AtolOnline/Tests/Entities/CompanyTest.php +++ b/tests/AtolOnline/Tests/Entities/CompanyTest.php @@ -44,7 +44,7 @@ class CompanyTest extends BasicTestCase */ public function testConstructor() { - $this->assertAtolable(new Company( + $this->assertIsAtolable(new Company( $email = 'company@example.com', $sno = SnoTypes::OSN, $inn = '1234567890', diff --git a/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php b/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php index e01cde6..3cf7e8d 100644 --- a/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php +++ b/tests/AtolOnline/Tests/Entities/CorrectionInfoTest.php @@ -43,7 +43,7 @@ class CorrectionInfoTest extends BasicTestCase */ public function testConstructor(): void { - $this->assertAtolable( + $this->assertIsAtolable( new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', $number = Helpers::randomStr()), [ 'type' => CorrectionTypes::SELF, diff --git a/tests/AtolOnline/Tests/Entities/ItemTest.php b/tests/AtolOnline/Tests/Entities/ItemTest.php index 35dc71e..3be1b50 100644 --- a/tests/AtolOnline/Tests/Entities/ItemTest.php +++ b/tests/AtolOnline/Tests/Entities/ItemTest.php @@ -71,7 +71,7 @@ class ItemTest extends BasicTestCase */ public function testConstructor(): void { - $this->assertAtolable( + $this->assertIsAtolable( new Item('test item', 2, 3), [ 'name' => 'test item', @@ -277,7 +277,7 @@ class ItemTest extends BasicTestCase PaymentObjects::COMMODITY, $item->setPaymentObject(PaymentObjects::COMMODITY)->getPaymentObject() ); - $this->assertAtolable($item, [ + $this->assertIsAtolable($item, [ 'name' => 'test item', 'price' => 2, 'quantity' => 3, @@ -349,7 +349,7 @@ class ItemTest extends BasicTestCase $this->assertIsSameClass(Vat::class, $item->getVat()); $this->assertEquals(VatTypes::VAT20, $item->getVat()->getType()); $this->assertEquals($item->getSum(), $item->getVat()->getSum()); - $this->assertAtolable($item, [ + $this->assertIsAtolable($item, [ 'name' => 'test item', 'price' => 2, 'quantity' => 3, @@ -384,7 +384,7 @@ class ItemTest extends BasicTestCase $this->assertIsSameClass(Vat::class, $item->getVat()); $this->assertEquals(VatTypes::VAT20, $item->getVat()->getType()); $this->assertEquals($item->getSum(), $item->getVat()->getSum()); - $this->assertAtolable($item, [ + $this->assertIsAtolable($item, [ 'name' => 'test item', 'price' => 2, 'quantity' => 3, @@ -472,7 +472,7 @@ class ItemTest extends BasicTestCase ); $item = (new Item('test item', 2, 3))->setSupplier($supplier); $this->assertEquals($supplier, $item->getSupplier()); - $this->assertAtolable($item, [ + $this->assertIsAtolable($item, [ 'name' => 'test item', 'price' => 2, 'quantity' => 3, @@ -502,7 +502,7 @@ class ItemTest extends BasicTestCase */ public function testValidUserdata(): void { - $this->assertAtolable( + $this->assertIsAtolable( (new Item('test item', 2, 3)) ->setUserData($user_data = Helpers::randomStr(Constraints::MAX_LENGTH_USER_DATA)), [ @@ -572,7 +572,7 @@ class ItemTest extends BasicTestCase */ public function testCountryCode(): void { - $this->assertAtolable( + $this->assertIsAtolable( (new Item('test item', 2, 3))->setCountryCode('800'), [ 'name' => 'test item', @@ -620,7 +620,7 @@ class ItemTest extends BasicTestCase */ public function testValidDeclarationNumber(): void { - $this->assertAtolable( + $this->assertIsAtolable( (new Item('test item', 2, 3)) ->setDeclarationNumber($code = Helpers::randomStr()), [ @@ -691,7 +691,7 @@ class ItemTest extends BasicTestCase */ public function testExcise(): void { - $this->assertAtolable( + $this->assertIsAtolable( (new Item('test item', 2, 3))->setExcise(1), [ 'name' => 'test item', @@ -750,7 +750,7 @@ class ItemTest extends BasicTestCase $decoded = hex2bin(str_replace(' ', '', $item->getCodeHex())); $this->assertEquals($decoded, $item->getCode()); - $this->assertAtolable($item, [ + $this->assertIsAtolable($item, [ 'name' => 'test item', 'price' => 2, 'quantity' => 3, diff --git a/tests/AtolOnline/Tests/Entities/KktEntityTest.php b/tests/AtolOnline/Tests/Entities/KktEntityTest.php index 6e6cbba..f27758e 100644 --- a/tests/AtolOnline/Tests/Entities/KktEntityTest.php +++ b/tests/AtolOnline/Tests/Entities/KktEntityTest.php @@ -57,7 +57,7 @@ class KktEntityTest extends BasicTestCase { $kkt = new Kkt((object)$this->sample_data); $this->assertIsSameClass(Kkt::class, $kkt); - $this->assertAtolable($kkt); + $this->assertIsAtolable($kkt); } /** diff --git a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php index 1416bfd..f077466 100644 --- a/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php +++ b/tests/AtolOnline/Tests/Entities/MoneyTransferOperatorTest.php @@ -51,11 +51,11 @@ class MoneyTransferOperatorTest extends BasicTestCase */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new MoneyTransferOperator('some name'), ['name' => 'some name']); - $this->assertAtolable(new MoneyTransferOperator(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); - $this->assertAtolable(new MoneyTransferOperator(address: 'London'), ['address' => 'London']); - $this->assertAtolable(new MoneyTransferOperator(phones: ['+122997365456']), ['phones' => ['+122997365456']]); - $this->assertAtolable(new MoneyTransferOperator( + $this->assertIsAtolable(new MoneyTransferOperator('some name'), ['name' => 'some name']); + $this->assertIsAtolable(new MoneyTransferOperator(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); + $this->assertIsAtolable(new MoneyTransferOperator(address: 'London'), ['address' => 'London']); + $this->assertIsAtolable(new MoneyTransferOperator(phones: ['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertIsAtolable(new MoneyTransferOperator( 'some name', '+fasd3\qe3fs_=nac99013928czc', 'London', diff --git a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php index 6e194e6..62ed0a4 100644 --- a/tests/AtolOnline/Tests/Entities/PayingAgentTest.php +++ b/tests/AtolOnline/Tests/Entities/PayingAgentTest.php @@ -49,18 +49,18 @@ class PayingAgentTest extends BasicTestCase public function testConstructorWithArgs(): void { $operation = Helpers::randomStr(); - $this->assertAtolable(new PayingAgent( + $this->assertIsAtolable(new PayingAgent( $operation, ['+122997365456'], ), [ 'operation' => $operation, 'phones' => ['+122997365456'], ]); - $this->assertAtolable( + $this->assertIsAtolable( new PayingAgent($operation), ['operation' => $operation] ); - $this->assertAtolable( + $this->assertIsAtolable( new PayingAgent(phones: ['+122997365456']), ['phones' => ['+122997365456']] ); diff --git a/tests/AtolOnline/Tests/Entities/PaymentTest.php b/tests/AtolOnline/Tests/Entities/PaymentTest.php index db1368b..da58d5a 100644 --- a/tests/AtolOnline/Tests/Entities/PaymentTest.php +++ b/tests/AtolOnline/Tests/Entities/PaymentTest.php @@ -42,7 +42,7 @@ class PaymentTest extends BasicTestCase */ public function testConstructor(): void { - $this->assertAtolable( + $this->assertIsAtolable( new Payment(PaymentTypes::ELECTRON, 123.456789), [ 'type' => PaymentTypes::ELECTRON, diff --git a/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php b/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php index b2b38af..8ed134d 100644 --- a/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceivePaymentsOperatorTest.php @@ -43,7 +43,7 @@ class ReceivePaymentsOperatorTest extends BasicTestCase */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new ReceivePaymentsOperator(['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertIsAtolable(new ReceivePaymentsOperator(['+122997365456']), ['phones' => ['+122997365456']]); } /** diff --git a/tests/AtolOnline/Tests/Entities/SupplierTest.php b/tests/AtolOnline/Tests/Entities/SupplierTest.php index b24d670..c14720c 100644 --- a/tests/AtolOnline/Tests/Entities/SupplierTest.php +++ b/tests/AtolOnline/Tests/Entities/SupplierTest.php @@ -49,10 +49,10 @@ class SupplierTest extends BasicTestCase */ public function testConstructorWithArgs(): void { - $this->assertAtolable(new Supplier('some name'), ['name' => 'some name']); - $this->assertAtolable(new Supplier(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); - $this->assertAtolable(new Supplier(phones: ['+122997365456']), ['phones' => ['+122997365456']]); - $this->assertAtolable(new Supplier( + $this->assertIsAtolable(new Supplier('some name'), ['name' => 'some name']); + $this->assertIsAtolable(new Supplier(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); + $this->assertIsAtolable(new Supplier(phones: ['+122997365456']), ['phones' => ['+122997365456']]); + $this->assertIsAtolable(new Supplier( 'some name', '+fasd3\qe3fs_=nac99013928czc', ['+122997365456'], diff --git a/tests/AtolOnline/Tests/Entities/VatTest.php b/tests/AtolOnline/Tests/Entities/VatTest.php index 40a2568..33d39cd 100644 --- a/tests/AtolOnline/Tests/Entities/VatTest.php +++ b/tests/AtolOnline/Tests/Entities/VatTest.php @@ -76,7 +76,7 @@ class VatTest extends BasicTestCase public function testConstructor(string $type, float $sum): void { $vat = new Vat($type, $sum); - $this->assertAtolable($vat, [ + $this->assertIsAtolable($vat, [ 'type' => $vat->getType(), 'sum' => $vat->getCalculated(), ]); From b39e76f31276a7107601073e816a84526251e00a Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Tue, 7 Dec 2021 20:09:12 +0800 Subject: [PATCH 64/85] =?UTF-8?q?=D0=92=D1=82=D0=BE=D1=80=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20`Receipt`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - фикс nullable-свойств и геттеров - проверка на пустоту в `setPayments()`, `setItems()` и `setVats()` - часть тестов с покрытием (конструктор, агент, исключения при пустых коллекциях) --- src/Entities/Receipt.php | 46 +++- src/Exceptions/EmptyItemsException.php | 22 ++ src/Exceptions/EmptyPaymentsException.php | 22 ++ src/Exceptions/EmptyVatsException.php | 22 ++ .../AtolOnline/Tests/Entities/ReceiptTest.php | 259 ++++++++++++++++++ 5 files changed, 358 insertions(+), 13 deletions(-) create mode 100644 src/Exceptions/EmptyItemsException.php create mode 100644 src/Exceptions/EmptyPaymentsException.php create mode 100644 src/Exceptions/EmptyVatsException.php create mode 100644 tests/AtolOnline/Tests/Entities/ReceiptTest.php diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index 8f1326b..dd9694f 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -15,7 +15,10 @@ use AtolOnline\Collections\Items; use AtolOnline\Collections\Payments; use AtolOnline\Collections\Vats; use AtolOnline\Constants\Constraints; -use AtolOnline\Exceptions\TooHighPriceException; +use AtolOnline\Exceptions\EmptyItemsException; +use AtolOnline\Exceptions\EmptyPaymentsException; +use AtolOnline\Exceptions\EmptyVatsException; +use AtolOnline\Exceptions\InvalidEntityInCollectionException; use AtolOnline\Exceptions\TooLongAddCheckPropException; use AtolOnline\Exceptions\TooLongCashierException; use Exception; @@ -75,22 +78,26 @@ class Receipt extends Entity /** * @var string|null Дополнительный реквизит */ - protected ?string $add_check_props; + protected ?string $add_check_props = null; /** * @var AdditionalUserProps|null Дополнительный реквизит пользователя */ - protected ?AdditionalUserProps $add_user_props; + protected ?AdditionalUserProps $add_user_props = null; /** * Конструктор + * + * @param Client $client + * @param Company $company + * @param Items $items + * @param Payments $payments + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException */ - public function __construct( - Client $client, - Company $company, - Items $items, - Payments $payments, - ) { + public function __construct(Client $client, Company $company, Items $items, Payments $payments) + { $this->setClient($client)->setCompany($company)->setItems($items)->setPayments($payments); } @@ -141,7 +148,7 @@ class Receipt extends Entity /** * Возвращает установленного агента * - * @return AgentInfo + * @return AgentInfo|null */ public function getAgentInfo(): ?AgentInfo { @@ -198,9 +205,15 @@ class Receipt extends Entity * @todo исключение при пустой коллекции * @param Items $items * @return Receipt + * @throws EmptyItemsException + * @throws InvalidEntityInCollectionException */ public function setItems(Items $items): self { + if ($items->isEmpty()) { + throw new EmptyItemsException(); + } + $items->checkItemsClasses(); $this->items = $items; return $this; } @@ -218,12 +231,15 @@ class Receipt extends Entity /** * Устанаваливает коллекцию оплат * - * @todo исключение при пустой коллекции * @param Payments $payments * @return Receipt + * @throws EmptyPaymentsException */ public function setPayments(Payments $payments): self { + if ($payments->isEmpty()) { + throw new EmptyPaymentsException(); + } $this->payments = $payments; return $this; } @@ -243,9 +259,13 @@ class Receipt extends Entity * * @param Vats|null $vats * @return Receipt + * @throws EmptyVatsException */ public function setVats(?Vats $vats): self { + if ($vats->isEmpty()) { + throw new EmptyVatsException(); + } $this->vats = $vats; return $this; } @@ -297,7 +317,7 @@ class Receipt extends Entity throw new TooLongCashierException($cashier); } } - $this->cashier = empty($cashier) ? null : $cashier; + $this->cashier = $cashier ?: null; return $this; } @@ -364,7 +384,7 @@ class Receipt extends Entity 'company' => $this->getCompany(), 'items' => $this->getItems(), 'total' => $this->getTotal(), - 'payment' => $this->getPayments(), + 'payments' => $this->getPayments(), ]; $this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo(); $this->getSupplier()?->jsonSerialize() && $json['vats'] = $this->getVats(); diff --git a/src/Exceptions/EmptyItemsException.php b/src/Exceptions/EmptyItemsException.php new file mode 100644 index 0000000..771aee2 --- /dev/null +++ b/src/Exceptions/EmptyItemsException.php @@ -0,0 +1,22 @@ +validReceipt(); + $this->assertIsAtolable($receipt); + } + + /** + * Тестирует установку данных агента + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setAgentInfo + * @covers \AtolOnline\Entities\Receipt::getAgentInfo + * @covers \AtolOnline\Entities\Receipt::jsonSerialize + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws InvalidInnLengthException + * @throws InvalidPhoneException + * @throws TooLongPayingAgentOperationException + * @throws Exception + */ + public function testSetAgentInfo(): void + { + $agent_info = new AgentInfo( + AgentTypes::ANOTHER, + new PayingAgent('test', ['+79518888888']), + new ReceivePaymentsOperator(['+79519999999']), + new MoneyTransferOperator('MTO Name', '9876543210', 'London', ['+79517777777']), + ); + $receipt = $this->validReceipt()->setAgentInfo($agent_info); + $this->assertArrayHasKey('agent_info', $receipt->jsonSerialize()); + $this->assertEquals($receipt->getAgentInfo(), $receipt->jsonSerialize()['agent_info']); + } + + /** + * Тестирует выброс исключения при передаче пустой коллекции предметов расчёта + * + * @return void + * @covers \AtolOnline\Entities\Receipt + * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Collections\Items::checkCount + * @covers \AtolOnline\Exceptions\EmptyItemsException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws InvalidEntityInCollectionException + * @throws EmptyPaymentsException + */ + public function testEmptyItemsException(): void + { + $this->expectException(EmptyItemsException::class); + new Receipt( + new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new Items([]), + new Payments($this->generatePaymentObjects()) + ); + } + + /** + * Тестирует выброс исключения при передаче коллекции предметов расчёта с некорректным содержимым + * + * @return void + * @covers \AtolOnline\Entities\Receipt + * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Collections\Items::checkItemsClasses + * @covers \AtolOnline\Collections\Items::checkItemClass + * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + */ + public function testInvalidItemInCollectionException(): void + { + $this->expectException(InvalidEntityInCollectionException::class); + $this->expectErrorMessage('Коллекция AtolOnline\Collections\Items должна содержать объекты AtolOnline\Entities\Item'); + new Receipt( + new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new Items(['qwerty']), + new Payments($this->generatePaymentObjects()) + ); + } + + /** + * Тестирует выброс исключения при передаче пустой коллекции оплат + * + * @return void + * @covers \AtolOnline\Entities\Receipt + * @covers \AtolOnline\Entities\Receipt::setPayments + * @covers \AtolOnline\Collections\Payments::checkCount + * @covers \AtolOnline\Exceptions\EmptyPaymentsException + * @throws TooHighPaymentSumException + * @throws EmptyItemNameException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws InvalidEntityInCollectionException + * @throws EmptyItemsException + */ + public function testEmptyPaymentsException(): void + { + $this->expectException(EmptyPaymentsException::class); + new Receipt( + new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new Items([new Item('test item', 2, 3)]), + new Payments([]) + ); + } + + /** + * Тестирует выброс исключения при передаче коллекции предметов расчёта с некорректным содержимым + * + * @return void + * @covers \AtolOnline\Entities\Receipt + * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Collections\Items::checkItemsClasses + * @covers \AtolOnline\Collections\Items::checkItemClass + * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws TooHighItemPriceException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testInvalidPaymentInCollectionException(): void + { + $this->expectException(InvalidEntityInCollectionException::class); + $this->expectErrorMessage('Коллекция AtolOnline\Collections\Payments должна содержать объекты AtolOnline\Entities\Payment'); + new Receipt( + new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new Items([new Item('test item', 2, 3)]), + new Payments(['qwerty']) + ); + } + + /** + * Возвращает валидный тестовый объект чека + * + * @return Receipt + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + */ + protected function validReceipt(): Receipt + { + return new Receipt( + new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new Items($this->generateItemObjects()), + new Payments($this->generatePaymentObjects()) + ); + } +} From b57acf8b05eed50bca6682bec986d663cf56ed5b Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 8 Dec 2021 15:50:06 +0800 Subject: [PATCH 65/85] =?UTF-8?q?`EntityCollection`=20=D0=BF=D0=BE=D0=BC?= =?UTF-8?q?=D0=B5=D1=87=D0=B5=D0=BD=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Collections/EntityCollection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Collections/EntityCollection.php b/src/Collections/EntityCollection.php index fc80b97..d66da8f 100644 --- a/src/Collections/EntityCollection.php +++ b/src/Collections/EntityCollection.php @@ -27,6 +27,8 @@ abstract class EntityCollection extends Collection public function __construct($items = []) { $this->checkCount($items); + //TODO следует переделать EntityCollection в обёртку над Collection, + // ибо ломает методы Collection, которые return new static $this->checkItemsClasses($items); parent::__construct($items); } From 793549aaac286fda265f801eefa524ec31c91035 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 8 Dec 2021 15:55:52 +0800 Subject: [PATCH 66/85] =?UTF-8?q?`Entity`=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=B8=D0=BC=D0=BF=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D1=83=D0=B5=D1=82=20`Arrayable`=20=D0=B8=20`ArrayA?= =?UTF-8?q?ccess`=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE=D0=B2=D0=BC=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20=D1=81=20?= =?UTF-8?q?=D0=B8=D0=BC=D0=BC=D1=83=D1=82=D0=B0=D0=B1=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=8B=D0=BC=D0=B8=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Collections/EntityCollection.php | 1 + src/Entities/Entity.php | 55 +++++++++++++++++++++++++++- src/Entities/Vat.php | 5 +-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/Collections/EntityCollection.php b/src/Collections/EntityCollection.php index d66da8f..080cb52 100644 --- a/src/Collections/EntityCollection.php +++ b/src/Collections/EntityCollection.php @@ -87,6 +87,7 @@ abstract class EntityCollection extends Collection */ public function checkCount(array $items = []): void { + //TODO проверять пустоту? if (count($items) > static::MAX_COUNT || $this->count() === static::MAX_COUNT) { throw new (static::EXCEPTION_CLASS)(static::MAX_COUNT); } diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index 490fdc8..e0eee84 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -11,14 +11,29 @@ declare(strict_types = 1); namespace AtolOnline\Entities; +use ArrayAccess; +use Illuminate\Contracts\Support\Arrayable; use JsonSerializable; use Stringable; /** * Абстрактное описание любой сущности, представляемой как json */ -abstract class Entity implements JsonSerializable, Stringable +abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayAccess { + /** + * @inheritDoc + */ + abstract public function jsonSerialize(): array; + + /** + * @inheritDoc + */ + public function toArray() + { + return $this->jsonSerialize(); + } + /** * Возвращает строковое представление json-структуры объекта * @@ -26,6 +41,42 @@ abstract class Entity implements JsonSerializable, Stringable */ public function __toString() { - return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE); + 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 . ' нельзя изменять как массив. Следует использовать сеттеры.' + ); } } diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index 69f737c..771380c 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -16,8 +16,7 @@ use AtolOnline\Exceptions\InvalidEnumValueException; use AtolOnline\Helpers; use JetBrains\PhpStorm\{ ArrayShape, - Pure -}; + Pure}; /** * Класс, описывающий ставку НДС @@ -97,7 +96,7 @@ final class Vat extends Entity } /** - * Возвращает sdрасчитанный итоговый размер ставки НДС в рублях + * Возвращает расчитанный итоговый размер ставки НДС в рублях * * @return float * @see https://nalog-nalog.ru/nds/nalogovaya_baza_nds/kak-schitat-nds-pravilno-vychislyaem-20-ot-summy-primer-algoritm/ From fdc64954f9f4d0c79304ff65868832dc8624e280 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 8 Dec 2021 16:01:25 +0800 Subject: [PATCH 67/85] =?UTF-8?q?=D0=A2=D1=80=D0=B5=D1=82=D1=8C=D1=8F=20?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20`Receipt`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - просчёт ставок секи в `setVats()` - просчёт суммы чека в `setItems()` - геттеры `getItems()` и `getVats()` возвращают пустую коллекцию, если в чеке они отсутствуют - фикс `vats => supplier_info` в `jsonSerialize()` - тесты поставщика, ставок, расчёта ставок и суммы чека --- src/Entities/Receipt.php | 41 ++--- .../AtolOnline/Tests/Entities/ReceiptTest.php | 152 +++++++++++++++++- 2 files changed, 153 insertions(+), 40 deletions(-) diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index dd9694f..db18db9 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -196,7 +196,7 @@ class Receipt extends Entity */ public function getItems(): Items { - return $this->items; + return $this->items ?? new Items(); } /** @@ -207,6 +207,7 @@ class Receipt extends Entity * @return Receipt * @throws EmptyItemsException * @throws InvalidEntityInCollectionException + * @throws Exception */ public function setItems(Items $items): self { @@ -215,6 +216,8 @@ class Receipt extends Entity } $items->checkItemsClasses(); $this->items = $items; + $this->getItems()->each(fn ($item) => $this->total += $item->getSum()); + $this->total = round($this->total, 2); return $this; } @@ -251,7 +254,7 @@ class Receipt extends Entity */ public function getVats(): ?Vats { - return $this->vats; + return $this->vats ?? new Vats(); } /** @@ -260,6 +263,7 @@ class Receipt extends Entity * @param Vats|null $vats * @return Receipt * @throws EmptyVatsException + * @throws Exception */ public function setVats(?Vats $vats): self { @@ -267,6 +271,8 @@ class Receipt extends Entity throw new EmptyVatsException(); } $this->vats = $vats; + /** @var Vat $vat */ + $this->getVats()->each(fn ($vat) => $vat->setSum($this->getTotal())); return $this; } @@ -280,18 +286,6 @@ class Receipt extends Entity return $this->total; } - /** - * Устанавливает полную сумму чека - * - * @param float $total - * @return Receipt - */ - public function setTotal(float $total): self - { - $this->total = $total; - return $this; - } - /** * Возвращает установленного кассира * @@ -387,28 +381,11 @@ class Receipt extends Entity 'payments' => $this->getPayments(), ]; $this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo(); - $this->getSupplier()?->jsonSerialize() && $json['vats'] = $this->getVats(); + $this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier(); $this->getVats()?->jsonSerialize() && $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(); return $json; } - - ///** - // * Пересчитывает, сохраняет и возвращает итоговую сумму чека по всем позициям (включая НДС). Тег ФФД - 1020. - // * - // * @return float - // * @throws Exception - // */ - //public function calcTotal(): float - //{ - // $sum = 0; - // $this->clearVats(); - // foreach ($this->items->get() as $item) { - // $sum += $item->calcSum(); - // $this->addVat(new Vat($item->getVat()->getType(), $item->getSum())); - // } - // return $this->total = round($sum, 2); - //} } diff --git a/tests/AtolOnline/Tests/Entities/ReceiptTest.php b/tests/AtolOnline/Tests/Entities/ReceiptTest.php index 57628b6..3075b3d 100644 --- a/tests/AtolOnline/Tests/Entities/ReceiptTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceiptTest.php @@ -12,6 +12,7 @@ namespace AtolOnline\Tests\Entities; use AtolOnline\{ Collections\Items, Collections\Payments, + Collections\Vats, Entities\AgentInfo, Entities\Client, Entities\Company, @@ -20,6 +21,8 @@ use AtolOnline\{ Entities\PayingAgent, Entities\Receipt, Entities\ReceivePaymentsOperator, + Entities\Supplier, + Entities\Vat, Enums\AgentTypes, Enums\SnoTypes, Tests\BasicTestCase}; @@ -27,6 +30,7 @@ use AtolOnline\Exceptions\{ EmptyItemNameException, EmptyItemsException, EmptyPaymentsException, + EmptyVatsException, InvalidEntityInCollectionException, InvalidEnumValueException, InvalidInnLengthException, @@ -35,6 +39,7 @@ use AtolOnline\Exceptions\{ NegativeItemQuantityException, NegativePaymentSumException, TooHighItemPriceException, + TooHighItemSumException, TooHighPaymentSumException, TooLongItemNameException, TooLongPayingAgentOperationException, @@ -76,8 +81,9 @@ class ReceiptTest extends BasicTestCase */ public function testConstructor(): void { - $receipt = $this->validReceipt(); + $receipt = $this->newReceipt(); $this->assertIsAtolable($receipt); + $receipt->getItems(); } /** @@ -104,7 +110,7 @@ class ReceiptTest extends BasicTestCase * @throws TooLongPayingAgentOperationException * @throws Exception */ - public function testSetAgentInfo(): void + public function testAgentInfo(): void { $agent_info = new AgentInfo( AgentTypes::ANOTHER, @@ -112,9 +118,42 @@ class ReceiptTest extends BasicTestCase new ReceivePaymentsOperator(['+79519999999']), new MoneyTransferOperator('MTO Name', '9876543210', 'London', ['+79517777777']), ); - $receipt = $this->validReceipt()->setAgentInfo($agent_info); + $receipt = $this->newReceipt()->setAgentInfo($agent_info); $this->assertArrayHasKey('agent_info', $receipt->jsonSerialize()); $this->assertEquals($receipt->getAgentInfo(), $receipt->jsonSerialize()['agent_info']); + $this->assertArrayNotHasKey('agent_info', $receipt->setAgentInfo(null)->jsonSerialize()); + } + + /** + * Тестирует установку данных поставщика + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setSupplier + * @covers \AtolOnline\Entities\Receipt::getSupplier + * @covers \AtolOnline\Entities\Receipt::jsonSerialize + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPhoneException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws Exception + */ + public function testSupplier(): void + { + $supplier = new Supplier('some name', '+fasd3\qe3fs_=nac99013928czc', ['+122997365456']); + $receipt = $this->newReceipt()->setSupplier($supplier); + $this->assertArrayHasKey('supplier_info', $receipt->jsonSerialize()); + $this->assertEquals($receipt->getSupplier(), $receipt->jsonSerialize()['supplier_info']); + $this->assertArrayNotHasKey('supplier_info', $receipt->setSupplier(null)->jsonSerialize()); } /** @@ -122,7 +161,7 @@ class ReceiptTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt - * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Entities\Receipt::setItems * @covers \AtolOnline\Collections\Items::checkCount * @covers \AtolOnline\Exceptions\EmptyItemsException * @throws InvalidEnumValueException @@ -147,7 +186,7 @@ class ReceiptTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt - * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Entities\Receipt::setItems * @covers \AtolOnline\Collections\Items::checkItemsClasses * @covers \AtolOnline\Collections\Items::checkItemClass * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException @@ -204,7 +243,7 @@ class ReceiptTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt - * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Entities\Receipt::setPayments * @covers \AtolOnline\Collections\Items::checkItemsClasses * @covers \AtolOnline\Collections\Items::checkItemClass * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException @@ -230,6 +269,103 @@ class ReceiptTest extends BasicTestCase ); } + /** + * Тестирует выброс исключения при передаче пустой коллекции ставок НДС + * + * @return void + * @covers \AtolOnline\Entities\Receipt + * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Collections\Vats::checkCount + * @covers \AtolOnline\Exceptions\EmptyVatsException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws EmptyVatsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testEmptyVatsException(): void + { + $this->expectException(EmptyVatsException::class); + $this->newReceipt()->setVats(new Vats([])); + } + + /** + * Тестирует выброс исключения при передаче коллекции ставок НДС с некорректным содержимым + * + * @return void + * @covers \AtolOnline\Entities\Receipt + * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Collections\Vats::checkItemsClasses + * @covers \AtolOnline\Collections\Vats::checkItemClass + * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws EmptyVatsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testInvalidVatInCollectionException(): void + { + $this->expectException(InvalidEntityInCollectionException::class); + $this->expectErrorMessage('Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat'); + $this->newReceipt()->setVats(new Vats(['qwerty'])); + } + + /** + * Тестирует просчёт общей суммы чека и ставок НДС + * + * @covers \AtolOnline\Entities\Receipt::setVats + * @covers \AtolOnline\Entities\Receipt::getVats + * @covers \AtolOnline\Entities\Receipt::getTotal + * @throws TooHighItemPriceException + * @throws NegativeItemPriceException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws TooHighItemSumException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws EmptyItemsException + * @throws EmptyItemNameException + * @throws TooManyException + * @throws NegativeItemQuantityException + * @throws TooLongItemNameException + * @throws Exception + */ + public function testCalculations(): void + { + $items_total = 0; + $receipt = $this->newReceipt(); + + //TODO при $receipt->getItems()->pluck('sum') стреляет InvalidEntityInCollectionException + // см. примечания в конструкторе EntityCollection + $receipt->getItems()->each(function ($item) use (&$items_total) { + /** @var Item $item */ + return $items_total += $item->getSum(); + }); + $this->assertEquals($items_total, $receipt->getTotal()); + + /** @var Vat $vat */ + $receipt->setVats(new Vats($this->generateVatObjects(2)))->getVats() + ->each(fn ($vat) => $this->assertEquals($items_total, $vat->getSum())); + } + /** * Возвращает валидный тестовый объект чека * @@ -247,12 +383,12 @@ class ReceiptTest extends BasicTestCase * @throws TooLongItemNameException * @throws TooManyException */ - protected function validReceipt(): Receipt + protected function newReceipt(): Receipt { return new Receipt( new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), - new Items($this->generateItemObjects()), + new Items($this->generateItemObjects(2)), new Payments($this->generatePaymentObjects()) ); } From 703c5178f5eef93da00a44b54738726b8f9ec0da Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 8 Dec 2021 16:11:39 +0800 Subject: [PATCH 68/85] =?UTF-8?q?=D0=9C=D0=B8=D0=BD=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B0=D0=BF=D0=B4=D0=B5=D0=B9=D1=82=D1=8B=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 4 ++-- composer.lock | 64 +++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/composer.json b/composer.json index 3e9a9a1..bdbbb02 100644 --- a/composer.json +++ b/composer.json @@ -32,8 +32,8 @@ } ], "support": { - "rss": "https://t.me/atolonline_php", - "chat": "https://t.me/+Rky7ia68fctjZmUy", + "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" diff --git a/composer.lock b/composer.lock index 95f535a..9f68c2c 100644 --- a/composer.lock +++ b/composer.lock @@ -68,16 +68,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.4.0", + "version": "7.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94" + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", "shasum": "" }, "require": { @@ -86,7 +86,7 @@ "guzzlehttp/psr7": "^1.8.3 || ^2.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2" + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" @@ -172,7 +172,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.0" + "source": "https://github.com/guzzle/guzzle/tree/7.4.1" }, "funding": [ { @@ -188,7 +188,7 @@ "type": "tidelift" } ], - "time": "2021-10-18T09:52:00+00:00" + "time": "2021-12-06T18:43:05+00:00" }, { "name": "guzzlehttp/promises", @@ -391,16 +391,16 @@ }, { "name": "illuminate/collections", - "version": "v8.74.0", + "version": "v8.75.0", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "f292b77824b42cd28decc7327e7c2e24c3806371" + "reference": "5a018387352afa2af30fd2be0a78c31e93295720" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/f292b77824b42cd28decc7327e7c2e24c3806371", - "reference": "f292b77824b42cd28decc7327e7c2e24c3806371", + "url": "https://api.github.com/repos/illuminate/collections/zipball/5a018387352afa2af30fd2be0a78c31e93295720", + "reference": "5a018387352afa2af30fd2be0a78c31e93295720", "shasum": "" }, "require": { @@ -441,20 +441,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-11-30T14:29:03+00:00" + "time": "2021-12-07T14:48:29+00:00" }, { "name": "illuminate/contracts", - "version": "v8.74.0", + "version": "v8.75.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "b0886ec05a63b204634d64d0b39d5b78a7c06f81" + "reference": "b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/b0886ec05a63b204634d64d0b39d5b78a7c06f81", - "reference": "b0886ec05a63b204634d64d0b39d5b78a7c06f81", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f", + "reference": "b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f", "shasum": "" }, "require": { @@ -489,11 +489,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-11-17T15:04:30+00:00" + "time": "2021-12-07T08:18:44+00:00" }, { "name": "illuminate/macroable", - "version": "v8.74.0", + "version": "v8.75.0", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -1171,25 +1171,25 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.0", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", + "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -1218,7 +1218,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0" }, "funding": [ { @@ -1234,7 +1234,7 @@ "type": "tidelift" } ], - "time": "2021-07-12T14:48:14+00:00" + "time": "2021-11-01T23:48:49+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2002,16 +2002,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.9", + "version": "9.2.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f301eb1453c9e7a1bc912ee8b0ea9db22c60223b" + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f301eb1453c9e7a1bc912ee8b0ea9db22c60223b", - "reference": "f301eb1453c9e7a1bc912ee8b0ea9db22c60223b", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", "shasum": "" }, "require": { @@ -2067,7 +2067,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.9" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" }, "funding": [ { @@ -2075,7 +2075,7 @@ "type": "github" } ], - "time": "2021-11-19T15:21:02+00:00" + "time": "2021-12-05T09:12:13+00:00" }, { "name": "phpunit/php-file-iterator", From 16d11468267cf2c8e7f3da42442726066e678bd7 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Wed, 8 Dec 2021 19:04:14 +0800 Subject: [PATCH 69/85] =?UTF-8?q?=D0=A7=D0=B5=D1=82=D0=B2=D1=91=D1=80?= =?UTF-8?q?=D1=82=D0=B0=D1=8F=20=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20`Receipt`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 100% покрытие - элвисы в разных сеттерах --- src/Entities/Client.php | 8 +- src/Entities/PayingAgent.php | 5 +- src/Entities/Receipt.php | 2 +- src/Traits/HasEmail.php | 2 +- src/Traits/HasInn.php | 2 +- .../AtolOnline/Tests/Entities/ReceiptTest.php | 235 ++++++++++++++++-- 6 files changed, 226 insertions(+), 28 deletions(-) diff --git a/src/Entities/Client.php b/src/Entities/Client.php index 983233b..6a1e10b 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -17,12 +17,10 @@ use AtolOnline\Exceptions\{ InvalidInnLengthException, InvalidPhoneException, TooLongClientNameException, - TooLongEmailException -}; + TooLongEmailException}; use AtolOnline\Traits\{ HasEmail, - HasInn -}; + HasInn}; use JetBrains\PhpStorm\Pure; /** @@ -94,7 +92,7 @@ final class Client extends Entity throw new TooLongClientNameException($name); } } - $this->name = empty($name) ? null : $name; + $this->name = $name ?: null; return $this; } diff --git a/src/Entities/PayingAgent.php b/src/Entities/PayingAgent.php index 4b6c028..7620382 100644 --- a/src/Entities/PayingAgent.php +++ b/src/Entities/PayingAgent.php @@ -14,8 +14,7 @@ namespace AtolOnline\Entities; use AtolOnline\Constants\Constraints; use AtolOnline\Exceptions\{ InvalidPhoneException, - TooLongPayingAgentOperationException -}; + TooLongPayingAgentOperationException}; use AtolOnline\Traits\HasPhones; use Illuminate\Support\Collection; @@ -64,7 +63,7 @@ final class PayingAgent extends Entity throw new TooLongPayingAgentOperationException($operation); } } - $this->operation = empty($operation) ? null : $operation; + $this->operation = $operation ?: null; return $this; } diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index db18db9..beb8b68 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -340,7 +340,7 @@ class Receipt extends Entity throw new TooLongAddCheckPropException($add_check_props); } } - $this->add_check_props = empty($add_check_props) ? null : $add_check_props; + $this->add_check_props = $add_check_props ?: null; return $this; } diff --git a/src/Traits/HasEmail.php b/src/Traits/HasEmail.php index e72b736..661902c 100644 --- a/src/Traits/HasEmail.php +++ b/src/Traits/HasEmail.php @@ -41,7 +41,7 @@ trait HasEmail throw new InvalidEmailException($email); } } - $this->email = empty($email) ? null : $email; + $this->email = $email ?: null; return $this; } diff --git a/src/Traits/HasInn.php b/src/Traits/HasInn.php index 01ae7d3..8160ac4 100644 --- a/src/Traits/HasInn.php +++ b/src/Traits/HasInn.php @@ -37,7 +37,7 @@ trait HasInn throw new InvalidInnLengthException($inn); } } - $this->inn = empty($inn) ? null : $inn; + $this->inn = $inn ?: null; return $this; } diff --git a/tests/AtolOnline/Tests/Entities/ReceiptTest.php b/tests/AtolOnline/Tests/Entities/ReceiptTest.php index 3075b3d..b4cc014 100644 --- a/tests/AtolOnline/Tests/Entities/ReceiptTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceiptTest.php @@ -10,22 +10,28 @@ namespace AtolOnline\Tests\Entities; use AtolOnline\{ - Collections\Items, - Collections\Payments, - Collections\Vats, - Entities\AgentInfo, - Entities\Client, - Entities\Company, - Entities\Item, - Entities\MoneyTransferOperator, - Entities\PayingAgent, - Entities\Receipt, - Entities\ReceivePaymentsOperator, - Entities\Supplier, - Entities\Vat, - Enums\AgentTypes, - Enums\SnoTypes, + Constants\Constraints, + Helpers, Tests\BasicTestCase}; +use AtolOnline\Collections\{ + Items, + Payments, + Vats,}; +use AtolOnline\Entities\{ + AdditionalUserProps, + AgentInfo, + Client, + Company, + Item, + MoneyTransferOperator, + PayingAgent, + Receipt, + ReceivePaymentsOperator, + Supplier, + Vat,}; +use AtolOnline\Enums\{ + AgentTypes, + SnoTypes,}; use AtolOnline\Exceptions\{ EmptyItemNameException, EmptyItemsException, @@ -41,6 +47,8 @@ use AtolOnline\Exceptions\{ TooHighItemPriceException, TooHighItemSumException, TooHighPaymentSumException, + TooLongAddCheckPropException, + TooLongCashierException, TooLongItemNameException, TooLongPayingAgentOperationException, TooManyException}; @@ -83,7 +91,6 @@ class ReceiptTest extends BasicTestCase { $receipt = $this->newReceipt(); $this->assertIsAtolable($receipt); - $receipt->getItems(); } /** @@ -363,7 +370,201 @@ class ReceiptTest extends BasicTestCase /** @var Vat $vat */ $receipt->setVats(new Vats($this->generateVatObjects(2)))->getVats() - ->each(fn ($vat) => $this->assertEquals($items_total, $vat->getSum())); + ->each(fn($vat) => $this->assertEquals($items_total, $vat->getSum())); + } + + /** + * Тестирует установку валидного кассира + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setCashier + * @covers \AtolOnline\Entities\Receipt::getCashier + * @covers \AtolOnline\Entities\Receipt::jsonSerialize + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws TooLongCashierException + * @throws Exception + */ + public function testCashier(): void + { + $receipt = $this->newReceipt()->setCashier(Helpers::randomStr()); + $this->assertArrayHasKey('cashier', $receipt->jsonSerialize()); + $this->assertEquals($receipt->getCashier(), $receipt->jsonSerialize()['cashier']); + } + + /** + * Тестирует обнуление кассира + * + * @param mixed $param + * @return void + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Receipt::setCashier + * @covers \AtolOnline\Entities\Receipt::getCashier + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongCashierException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testNullableCashier(mixed $param): void + { + $this->assertNull($this->newReceipt()->setCashier($param)->getCashier()); + } + + /** + * Тестирует выброс исключения при установке слишком длинного кассира (лол) + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setCashier + * @covers \AtolOnline\Exceptions\TooLongCashierException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongCashierException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testTooLongCashierException(): void + { + $this->expectException(TooLongCashierException::class); + $this->newReceipt()->setCashier(Helpers::randomStr(Constraints::MAX_LENGTH_CASHIER_NAME + 1)); + } + + /** + * Тестирует установку дополнительного реквизита чека + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setAddCheckProps + * @covers \AtolOnline\Entities\Receipt::getAddCheckProps + * @covers \AtolOnline\Entities\Receipt::jsonSerialize + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws TooLongAddCheckPropException + * @throws Exception + */ + public function testAddCheckProps(): void + { + $receipt = $this->newReceipt()->setAddCheckProps(Helpers::randomStr()); + $this->assertArrayHasKey('additional_check_props', $receipt->jsonSerialize()); + $this->assertEquals($receipt->getAddCheckProps(), $receipt->jsonSerialize()['additional_check_props']); + } + + /** + * Тестирует обнуление дополнительного реквизита чека + * + * @param mixed $param + * @return void + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Receipt::setAddCheckProps + * @covers \AtolOnline\Entities\Receipt::getAddCheckProps + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongAddCheckPropException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testNullableAddCheckProps(mixed $param): void + { + $this->assertNull($this->newReceipt()->setAddCheckProps($param)->getAddCheckProps()); + } + + /** + * Тестирует выброс исключения при установке слишком длинного дополнительного реквизита чека + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setAddCheckProps + * @covers \AtolOnline\Exceptions\TooLongAddCheckPropException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongAddCheckPropException + * @throws TooLongItemNameException + * @throws TooManyException + */ + public function testTooLongAddCheckPropException(): void + { + $this->expectException(TooLongAddCheckPropException::class); + $this->newReceipt()->setAddCheckProps(Helpers::randomStr(Constraints::MAX_LENGTH_ADD_CHECK_PROP + 1)); + } + + /** + * Тестирует установку дополнительного реквизита пользователя + * + * @return void + * @covers \AtolOnline\Entities\Receipt::setAddUserProps + * @covers \AtolOnline\Entities\Receipt::getAddUserProps + * @covers \AtolOnline\Entities\Receipt::jsonSerialize + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + * @throws Exception + */ + public function testAdditionalUserProps(): void + { + $aup = new AdditionalUserProps('name', 'value'); + $receipt = $this->newReceipt()->setAddUserProps($aup); + $this->assertArrayHasKey('additional_user_props', $receipt->jsonSerialize()); + $this->assertEquals($receipt->getAddUserProps(), $receipt->jsonSerialize()['additional_user_props']); + $this->assertArrayNotHasKey('additional_user_props', $receipt->setAddUserProps(null)->jsonSerialize()); } /** From 058ce5ed3dc0bb9a07d37f11caf5ee6f81cc865f Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 9 Dec 2021 20:13:43 +0800 Subject: [PATCH 70/85] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B9,=20=D1=87=D0=B5=D0=BA=D0=B0=20=D0=B8=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `EntityCollection` сильно упрощён, добавлен выброс исключений при пустом содержимом - `Receipt::setItems(), setPayments() и setVats()` получили одинаковые проверки входящих данных - округление в `Vat::setSum()` - доработаны тесты коллекций --- src/Collections/EntityCollection.php | 69 ++------ src/Collections/Items.php | 8 +- src/Collections/Payments.php | 8 +- src/Collections/Vats.php | 8 +- src/Entities/Receipt.php | 30 ++-- src/Entities/Vat.php | 2 +- .../Tests/Collections/ItemsTest.php | 94 ++-------- .../Tests/Collections/PaymentsTest.php | 77 +-------- .../AtolOnline/Tests/Collections/VatsTest.php | 163 +++--------------- .../AtolOnline/Tests/Entities/ReceiptTest.php | 8 +- 10 files changed, 97 insertions(+), 370 deletions(-) diff --git a/src/Collections/EntityCollection.php b/src/Collections/EntityCollection.php index 080cb52..3552583 100644 --- a/src/Collections/EntityCollection.php +++ b/src/Collections/EntityCollection.php @@ -12,7 +12,6 @@ declare(strict_types = 1); namespace AtolOnline\Collections; use AtolOnline\Exceptions\InvalidEntityInCollectionException; -use Exception; use Illuminate\Support\Collection; /** @@ -21,60 +20,12 @@ use Illuminate\Support\Collection; abstract class EntityCollection extends Collection { /** - * @inheritDoc + * @return array * @throws InvalidEntityInCollectionException */ - public function __construct($items = []) - { - $this->checkCount($items); - //TODO следует переделать EntityCollection в обёртку над Collection, - // ибо ломает методы Collection, которые return new static - $this->checkItemsClasses($items); - parent::__construct($items); - } - - /** - * @inheritDoc - */ - public function prepend($value, $key = null): self - { - $this->checkCount(); - return parent::prepend($value, $key); - } - - /** - * @inheritDoc - */ - public function add($item): self - { - $this->checkCount(); - return parent::add($item); - } - - /** - * @inheritDoc - */ - public function push(...$values): self - { - $this->checkCount(); - return parent::push(...$values); - } - - /** - * @inheritDoc - */ - public function merge($items): self - { - $this->checkCount(); - return parent::merge($items); - } - - /** - * @inheritDoc - * @throws Exception - */ public function jsonSerialize(): array { + $this->checkCount(); $this->checkItemsClasses(); return parent::jsonSerialize(); } @@ -82,20 +33,19 @@ abstract class EntityCollection extends Collection /** * Проверяет количество элементов коллекции * - * @param array $items Массив элементов, если пустой - проверит содержимое коллекции * @return void */ - public function checkCount(array $items = []): void + public function checkCount(): void { - //TODO проверять пустоту? - if (count($items) > static::MAX_COUNT || $this->count() === static::MAX_COUNT) { - throw new (static::EXCEPTION_CLASS)(static::MAX_COUNT); - } + $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 @@ -108,10 +58,11 @@ abstract class EntityCollection extends Collection /** * Проверяет корректность классов элементов коллекции * + * @return $this * @throws InvalidEntityInCollectionException */ - public function checkItemsClasses(array $items = []): void + public function checkItemsClasses(): self { - (empty($items) ? $this : collect($items))->each(fn ($item) => $this->checkItemClass($item)); + return $this->each(fn($item) => $this->checkItemClass($item)); } } diff --git a/src/Collections/Items.php b/src/Collections/Items.php index 00e740c..62a1d06 100644 --- a/src/Collections/Items.php +++ b/src/Collections/Items.php @@ -11,6 +11,7 @@ namespace AtolOnline\Collections; use AtolOnline\Constants\Constraints; use AtolOnline\Entities\Item; +use AtolOnline\Exceptions\EmptyItemsException; use AtolOnline\Exceptions\TooManyItemsException; /** @@ -28,8 +29,13 @@ final class Items extends EntityCollection */ protected const MAX_COUNT = Constraints::MAX_COUNT_DOC_ITEMS; + /** + * Класс исключения для выброса при пустой коллекции + */ + protected const EMPTY_EXCEPTION_CLASS = EmptyItemsException::class; + /** * Класс-наследник TooManyException для выброса при превышении количества */ - protected const EXCEPTION_CLASS = TooManyItemsException::class; + protected const TOO_MANY_EXCEPTION_CLASS = TooManyItemsException::class; } diff --git a/src/Collections/Payments.php b/src/Collections/Payments.php index 00ce8b8..229aa26 100644 --- a/src/Collections/Payments.php +++ b/src/Collections/Payments.php @@ -11,6 +11,7 @@ namespace AtolOnline\Collections; use AtolOnline\Constants\Constraints; use AtolOnline\Entities\Payment; +use AtolOnline\Exceptions\EmptyPaymentsException; use AtolOnline\Exceptions\TooManyPaymentsException; /** @@ -28,8 +29,13 @@ final class Payments extends EntityCollection */ protected const MAX_COUNT = Constraints::MAX_COUNT_DOC_PAYMENTS; + /** + * Класс исключения для выброса при пустой коллекции + */ + protected const EMPTY_EXCEPTION_CLASS = EmptyPaymentsException::class; + /** * Класс-наследник TooManyException для выброса при превышении количества */ - protected const EXCEPTION_CLASS = TooManyPaymentsException::class; + protected const TOO_MANY_EXCEPTION_CLASS = TooManyPaymentsException::class; } diff --git a/src/Collections/Vats.php b/src/Collections/Vats.php index 8630568..127bb14 100644 --- a/src/Collections/Vats.php +++ b/src/Collections/Vats.php @@ -11,6 +11,7 @@ namespace AtolOnline\Collections; use AtolOnline\Constants\Constraints; use AtolOnline\Entities\Vat; +use AtolOnline\Exceptions\EmptyVatsException; use AtolOnline\Exceptions\TooManyVatsException; /** @@ -28,8 +29,13 @@ final class Vats extends EntityCollection */ protected const MAX_COUNT = Constraints::MAX_COUNT_DOC_VATS; + /** + * Класс исключения для выброса при пустой коллекции + */ + protected const EMPTY_EXCEPTION_CLASS = EmptyVatsException::class; + /** * Класс-наследник TooManyException для выброса при превышении количества */ - protected const EXCEPTION_CLASS = TooManyVatsException::class; + protected const TOO_MANY_EXCEPTION_CLASS = TooManyVatsException::class; } diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index beb8b68..632d634 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -16,8 +16,6 @@ use AtolOnline\Collections\Payments; use AtolOnline\Collections\Vats; use AtolOnline\Constants\Constraints; use AtolOnline\Exceptions\EmptyItemsException; -use AtolOnline\Exceptions\EmptyPaymentsException; -use AtolOnline\Exceptions\EmptyVatsException; use AtolOnline\Exceptions\InvalidEntityInCollectionException; use AtolOnline\Exceptions\TooLongAddCheckPropException; use AtolOnline\Exceptions\TooLongCashierException; @@ -93,7 +91,6 @@ class Receipt extends Entity * @param Items $items * @param Payments $payments * @throws EmptyItemsException - * @throws EmptyPaymentsException * @throws InvalidEntityInCollectionException */ public function __construct(Client $client, Company $company, Items $items, Payments $payments) @@ -211,12 +208,10 @@ class Receipt extends Entity */ public function setItems(Items $items): self { - if ($items->isEmpty()) { - throw new EmptyItemsException(); - } + $items->checkCount(); $items->checkItemsClasses(); $this->items = $items; - $this->getItems()->each(fn ($item) => $this->total += $item->getSum()); + $this->getItems()->each(fn($item) => $this->total += $item->getSum()); $this->total = round($this->total, 2); return $this; } @@ -236,13 +231,12 @@ class Receipt extends Entity * * @param Payments $payments * @return Receipt - * @throws EmptyPaymentsException + * @throws InvalidEntityInCollectionException */ public function setPayments(Payments $payments): self { - if ($payments->isEmpty()) { - throw new EmptyPaymentsException(); - } + $payments->checkCount(); + $payments->checkItemsClasses(); $this->payments = $payments; return $this; } @@ -262,17 +256,15 @@ class Receipt extends Entity * * @param Vats|null $vats * @return Receipt - * @throws EmptyVatsException * @throws Exception */ public function setVats(?Vats $vats): self { - if ($vats->isEmpty()) { - throw new EmptyVatsException(); - } + $vats->checkCount(); + $vats->checkItemsClasses(); $this->vats = $vats; /** @var Vat $vat */ - $this->getVats()->each(fn ($vat) => $vat->setSum($this->getTotal())); + $this->getVats()->each(fn($vat) => $vat->setSum($this->getTotal())); return $this; } @@ -376,13 +368,13 @@ class Receipt extends Entity $json = [ 'client' => $this->getClient(), 'company' => $this->getCompany(), - 'items' => $this->getItems(), + 'items' => $this->getItems()->jsonSerialize(), 'total' => $this->getTotal(), - 'payments' => $this->getPayments(), + 'payments' => $this->getPayments()->jsonSerialize(), ]; $this->getAgentInfo()?->jsonSerialize() && $json['agent_info'] = $this->getAgentInfo(); $this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier(); - $this->getVats()?->jsonSerialize() && $json['vats'] = $this->getVats(); + $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(); diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index 771380c..5b16069 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -91,7 +91,7 @@ final class Vat extends Entity */ public function setSum(float $rubles): self { - $this->sum = $rubles; + $this->sum = round($rubles, 2); return $this; } diff --git a/tests/AtolOnline/Tests/Collections/ItemsTest.php b/tests/AtolOnline/Tests/Collections/ItemsTest.php index 901001b..6cc9ace 100644 --- a/tests/AtolOnline/Tests/Collections/ItemsTest.php +++ b/tests/AtolOnline/Tests/Collections/ItemsTest.php @@ -15,6 +15,7 @@ use AtolOnline\{ Tests\BasicTestCase}; use AtolOnline\Exceptions\{ EmptyItemNameException, + EmptyItemsException, InvalidEntityInCollectionException, NegativeItemPriceException, NegativeItemQuantityException, @@ -29,10 +30,12 @@ use AtolOnline\Exceptions\{ class ItemsTest extends BasicTestCase { /** - * Тестирует выброс исключения при установке слишком большого количества оплат через конструктор + * Тестирует выброс исключения при установке слишком большого количества предметов расчёта * * @covers \AtolOnline\Collections\EntityCollection * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @covers \AtolOnline\Exceptions\TooManyItemsException * @throws EmptyItemNameException * @throws NegativeItemPriceException @@ -42,96 +45,25 @@ class ItemsTest extends BasicTestCase * @throws TooManyException * @throws InvalidEntityInCollectionException */ - public function testTooManyItemsExceptionByConstructor() + public function testTooManyItemsException() { $this->expectException(TooManyItemsException::class); - new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS + 1)); + (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS + 1)))->jsonSerialize(); } /** - * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection::prepend - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyItemsException - * @throws EmptyItemNameException - * @throws NegativeItemPriceException - * @throws NegativeItemQuantityException - * @throws TooHighItemPriceException - * @throws TooLongItemNameException - * @throws TooManyException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyItemsExceptionByPrepend() - { - $this->expectException(TooManyItemsException::class); - (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->prepend($this->generateItemObjects()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции + * Тестирует выброс исключения при установке нулевого количества предметов расчёта * * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::add * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyItemsException - * @throws EmptyItemNameException - * @throws NegativeItemPriceException - * @throws NegativeItemQuantityException - * @throws TooHighItemPriceException - * @throws TooLongItemNameException - * @throws TooManyException + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize + * @covers \AtolOnline\Exceptions\EmptyItemsException * @throws InvalidEntityInCollectionException */ - public function testTooManyItemsExceptionByAdd() + public function testEmptyItemsException() { - $this->expectException(TooManyItemsException::class); - (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->add($this->generateItemObjects()); - } - - /** - * Тестирует выброс исключения при добавлении лишних оплат в конец коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::push - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyItemsException - * @throws EmptyItemNameException - * @throws NegativeItemPriceException - * @throws NegativeItemQuantityException - * @throws TooHighItemPriceException - * @throws TooLongItemNameException - * @throws TooManyException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyItemsExceptionByPush() - { - $this->expectException(TooManyItemsException::class); - (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->push(...$this->generateItemObjects()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::merge - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyItemsException - * @throws EmptyItemNameException - * @throws NegativeItemPriceException - * @throws NegativeItemQuantityException - * @throws TooHighItemPriceException - * @throws TooLongItemNameException - * @throws TooManyException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyItemsExceptionByMerge() - { - $this->expectException(TooManyItemsException::class); - (new Items($this->generateItemObjects(Constraints::MAX_COUNT_DOC_ITEMS))) - ->merge($this->generateItemObjects(2)); + $this->expectException(EmptyItemsException::class); + (new Items([]))->jsonSerialize(); } } diff --git a/tests/AtolOnline/Tests/Collections/PaymentsTest.php b/tests/AtolOnline/Tests/Collections/PaymentsTest.php index 94882ae..dd1a14e 100644 --- a/tests/AtolOnline/Tests/Collections/PaymentsTest.php +++ b/tests/AtolOnline/Tests/Collections/PaymentsTest.php @@ -12,6 +12,7 @@ namespace AtolOnline\Tests\Collections; use AtolOnline\{ Collections\Payments, Constants\Constraints, + Exceptions\EmptyPaymentsException, Exceptions\InvalidEntityInCollectionException, Exceptions\InvalidEnumValueException, Exceptions\NegativePaymentSumException, @@ -25,7 +26,7 @@ use AtolOnline\{ class PaymentsTest extends BasicTestCase { /** - * Тестирует выброс исключения при установке слишком большого количества оплат через конструктор + * Тестирует выброс исключения при установке слишком большого количества оплат * * @covers \AtolOnline\Collections\EntityCollection * @covers \AtolOnline\Collections\EntityCollection::checkCount @@ -38,81 +39,21 @@ class PaymentsTest extends BasicTestCase public function testTooManyPaymentsExceptionByConstructor() { $this->expectException(TooManyPaymentsException::class); - new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1)); + (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1)))->jsonSerialize(); } /** - * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection::prepend - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyPaymentsException - * @throws InvalidEnumValueException - * @throws NegativePaymentSumException - * @throws TooHighPaymentSumException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyPaymentsExceptionByPrepend() - { - $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) - ->prepend($this->generatePaymentObjects()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции + * Тестирует выброс исключения при установке нулевого количества оплат * * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::add * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyPaymentsException - * @throws InvalidEnumValueException - * @throws NegativePaymentSumException - * @throws TooHighPaymentSumException + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize + * @covers \AtolOnline\Exceptions\EmptyPaymentsException * @throws InvalidEntityInCollectionException */ - public function testTooManyPaymentsExceptionByAdd() + public function testEmptyPaymentsException() { - $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS))) - ->add($this->generatePaymentObjects()); - } - - /** - * Тестирует выброс исключения при добавлении лишних оплат в конец коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::push - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyPaymentsException - * @throws InvalidEnumValueException - * @throws NegativePaymentSumException - * @throws TooHighPaymentSumException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyPaymentsExceptionByPush() - { - $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS + 1))) - ->push(...$this->generatePaymentObjects()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::merge - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyPaymentsException - * @throws InvalidEnumValueException - * @throws NegativePaymentSumException - * @throws TooHighPaymentSumException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyPaymentsExceptionByMerge() - { - $this->expectException(TooManyPaymentsException::class); - (new Payments($this->generatePaymentObjects(Constraints::MAX_COUNT_DOC_PAYMENTS - 1))) - ->merge($this->generatePaymentObjects(2)); + $this->expectException(EmptyPaymentsException::class); + (new Payments([]))->jsonSerialize(); } } diff --git a/tests/AtolOnline/Tests/Collections/VatsTest.php b/tests/AtolOnline/Tests/Collections/VatsTest.php index 7a20a50..cafadc6 100644 --- a/tests/AtolOnline/Tests/Collections/VatsTest.php +++ b/tests/AtolOnline/Tests/Collections/VatsTest.php @@ -14,6 +14,7 @@ use AtolOnline\{ Constants\Constraints, Entities\Payment, Enums\PaymentTypes, + Exceptions\EmptyVatsException, Exceptions\InvalidEntityInCollectionException, Exceptions\InvalidEnumValueException, Exceptions\NegativePaymentSumException, @@ -32,6 +33,8 @@ class VatsTest extends BasicTestCase * * @covers \AtolOnline\Collections\EntityCollection * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @throws InvalidEnumValueException * @throws Exception */ @@ -44,150 +47,36 @@ class VatsTest extends BasicTestCase } /** - * Тестирует выброс исключения при установке слишком большого количества ставок через конструктор + * Тестирует выброс исключения при установке нулевого количества ставок * * @covers \AtolOnline\Collections\EntityCollection * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize + * @covers \AtolOnline\Exceptions\EmptyVatsException + * @throws InvalidEntityInCollectionException + */ + public function testEmptyVatsException() + { + $this->expectException(EmptyVatsException::class); + (new Vats([]))->jsonSerialize(); + } + + /** + * Тестирует выброс исключения при установке слишком большого количества ставок + * + * @covers \AtolOnline\Collections\EntityCollection + * @covers \AtolOnline\Collections\EntityCollection::checkCount + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @covers \AtolOnline\Exceptions\TooManyVatsException * @throws InvalidEnumValueException * @throws InvalidEntityInCollectionException */ - public function testTooManyVatsExceptionByConstructor() + public function testTooManyVatsException() { $this->expectException(TooManyVatsException::class); - new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS + 1)); - } - - /** - * Тестирует добавление ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::prepend - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testPrepend() - { - $vats = (new Vats($this->generateVatObjects(3))) - ->prepend($this->generateVatObjects()); - $this->assertEquals(4, $vats->count()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::prepend - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyVatsException - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyVatsExceptionByPrepend() - { - $this->expectException(TooManyVatsException::class); - (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS))) - ->prepend($this->generateVatObjects()); - } - - /** - * Тестирует добавление ставки в конец коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::add - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testAdd() - { - $vats = (new Vats($this->generateVatObjects(3))) - ->add($this->generateVatObjects()); - $this->assertEquals(4, $vats->count()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в конец коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::add - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyVatsException - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyVatsExceptionByAdd() - { - $this->expectException(TooManyVatsException::class); - (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS))) - ->add($this->generateVatObjects()); - } - - /** - * Тестирует добавление лишних ставок в конец коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::push - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testPush() - { - $vats = (new Vats($this->generateVatObjects(3))) - ->push(...$this->generateVatObjects(3)); - $this->assertEquals(6, $vats->count()); - } - - /** - * Тестирует выброс исключения при добавлении лишних ставок в конец коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::push - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyVatsException - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyVatsExceptionByPush() - { - $this->expectException(TooManyVatsException::class); - (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS))) - ->push(...$this->generateVatObjects()); - } - - /** - * Тестирует добавление ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::merge - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testMerge() - { - $vats = (new Vats($this->generateVatObjects(3))) - ->merge($this->generateVatObjects(3)); - $this->assertEquals(6, $vats->count()); - } - - /** - * Тестирует выброс исключения при добавлении лишней ставки в начало коллекции - * - * @covers \AtolOnline\Collections\EntityCollection - * @covers \AtolOnline\Collections\EntityCollection::merge - * @covers \AtolOnline\Collections\EntityCollection::checkCount - * @covers \AtolOnline\Exceptions\TooManyVatsException - * @throws InvalidEnumValueException - * @throws InvalidEntityInCollectionException - */ - public function testTooManyVatsExceptionByMerge() - { - $this->expectException(TooManyVatsException::class); - (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS - 1))) - ->merge($this->generateVatObjects(2)); + (new Vats($this->generateVatObjects(Constraints::MAX_COUNT_DOC_VATS + 1)))->jsonSerialize(); } /** @@ -195,6 +84,7 @@ class VatsTest extends BasicTestCase * * @covers \AtolOnline\Collections\EntityCollection * @covers \AtolOnline\Collections\EntityCollection::checkItemClass + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @covers \AtolOnline\Exceptions\InvalidEntityInCollectionException * @throws InvalidEnumValueException @@ -212,6 +102,9 @@ class VatsTest extends BasicTestCase /** * Тестирует выброс исключения при наличии объектов не тех классов в коллекции * + * @covers \AtolOnline\Collections\EntityCollection::checkItemClass + * @covers \AtolOnline\Collections\EntityCollection::checkItemsClasses + * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @throws InvalidEnumValueException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException diff --git a/tests/AtolOnline/Tests/Entities/ReceiptTest.php b/tests/AtolOnline/Tests/Entities/ReceiptTest.php index b4cc014..5665af5 100644 --- a/tests/AtolOnline/Tests/Entities/ReceiptTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceiptTest.php @@ -268,7 +268,7 @@ class ReceiptTest extends BasicTestCase { $this->expectException(InvalidEntityInCollectionException::class); $this->expectErrorMessage('Коллекция AtolOnline\Collections\Payments должна содержать объекты AtolOnline\Entities\Payment'); - new Receipt( + (string)new Receipt( new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), new Items([new Item('test item', 2, 3)]), @@ -287,7 +287,6 @@ class ReceiptTest extends BasicTestCase * @throws EmptyItemNameException * @throws EmptyItemsException * @throws EmptyPaymentsException - * @throws EmptyVatsException * @throws InvalidEntityInCollectionException * @throws InvalidEnumValueException * @throws NegativeItemPriceException @@ -297,6 +296,7 @@ class ReceiptTest extends BasicTestCase * @throws TooHighPaymentSumException * @throws TooLongItemNameException * @throws TooManyException + * @throws Exception */ public function testEmptyVatsException(): void { @@ -316,7 +316,6 @@ class ReceiptTest extends BasicTestCase * @throws EmptyItemNameException * @throws EmptyItemsException * @throws EmptyPaymentsException - * @throws EmptyVatsException * @throws InvalidEntityInCollectionException * @throws InvalidEnumValueException * @throws NegativeItemPriceException @@ -326,12 +325,13 @@ class ReceiptTest extends BasicTestCase * @throws TooHighPaymentSumException * @throws TooLongItemNameException * @throws TooManyException + * @throws Exception */ public function testInvalidVatInCollectionException(): void { $this->expectException(InvalidEntityInCollectionException::class); $this->expectErrorMessage('Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat'); - $this->newReceipt()->setVats(new Vats(['qwerty'])); + (string)$this->newReceipt()->setVats(new Vats(['qwerty'])); } /** From 1d6abfd475e46390ed2745104bbec88dab07b89a Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 9 Dec 2021 20:14:16 +0800 Subject: [PATCH 71/85] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D0=B5=D0=B5=20=D1=81?= =?UTF-8?q?=D0=B8=D0=BC=D0=BF=D0=B0=D1=82=D0=B8=D1=87=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=B1=D0=B5=D0=B9=D0=B4=D0=B6=D0=B8=D0=BA=D0=B8=20=D0=B2=20REA?= =?UTF-8?q?DME?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a600739..c7c344d 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ [![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) -[![Total Downloads](http://poser.pugx.org/axenov/atol-online/downloads)](https://packagist.org/packages/axenov/atol-online) -[![License](http://poser.pugx.org/axenov/atol-online/license)](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/). @@ -16,9 +17,9 @@ --- -| master | [![CI](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=master)](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 | [![CI dev](https://github.com/anthonyaxenov/atol-online/actions/workflows/ci.yml/badge.svg?branch=dev)](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) | +| 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) | Текущие поддерживаемые версии АТОЛ Онлайн: From 6787ce3ad72dac90930d99dd2cf52b07724532e1 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 11 Dec 2021 15:53:57 +0800 Subject: [PATCH 72/85] =?UTF-8?q?=D0=9A=D0=BB=D0=B0=D1=81=D1=81=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=20=D0=BA=D0=BE?= =?UTF-8?q?=D1=80=D1=80=D0=B5=D0=BA=D1=86=D0=B8=D0=B8=20`Correction`=20?= =?UTF-8?q?=D1=81=20=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B5=D0=BC?= =?UTF-8?q?=20=D0=B8=20=D0=B2=D1=81=D1=8F=D0=BA=D0=B0=D1=8F=20=D0=BC=D0=B5?= =?UTF-8?q?=D0=BB=D0=BE=D1=87=D1=91=D0=B2=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - финализация Receipt + Payment - фиксы phpdoc --- src/Collections/EntityCollection.php | 2 +- src/Entities/Company.php | 16 +- src/Entities/Correction.php | 215 ++++++++++++++++++ src/Entities/Payment.php | 11 +- src/Entities/Receipt.php | 27 ++- src/Enums/DocumentTypes.php | 4 +- .../Tests/Entities/CorrectionTest.php | 138 +++++++++++ 7 files changed, 380 insertions(+), 33 deletions(-) create mode 100644 src/Entities/Correction.php create mode 100644 tests/AtolOnline/Tests/Entities/CorrectionTest.php diff --git a/src/Collections/EntityCollection.php b/src/Collections/EntityCollection.php index 3552583..7c92f0d 100644 --- a/src/Collections/EntityCollection.php +++ b/src/Collections/EntityCollection.php @@ -20,7 +20,7 @@ use Illuminate\Support\Collection; abstract class EntityCollection extends Collection { /** - * @return array + * @inheritDoc * @throws InvalidEntityInCollectionException */ public function jsonSerialize(): array diff --git a/src/Entities/Company.php b/src/Entities/Company.php index e5d9804..ed665ca 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -11,18 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\{ - Constants\Constraints, - Enums\SnoTypes, - Traits\HasEmail, - Traits\HasInn}; -use AtolOnline\Exceptions\{ - InvalidEmailException, +use AtolOnline\{Constants\Constraints, Enums\SnoTypes, Traits\HasEmail, Traits\HasInn}; +use AtolOnline\Exceptions\{InvalidEmailException, InvalidEnumValueException, InvalidInnLengthException, InvalidPaymentAddressException, TooLongEmailException, - TooLongPaymentAddressException}; + TooLongPaymentAddressException +}; use JetBrains\PhpStorm\ArrayShape; /** @@ -59,8 +55,8 @@ final class Company extends Entity * @throws TooLongPaymentAddressException */ public function __construct( - string $email, - string $sno, + string $email, //TODO сделать необязательным здесь + string $sno, //TODO сделать необязательным здесь string $inn, string $payment_address, ) { diff --git a/src/Entities/Correction.php b/src/Entities/Correction.php new file mode 100644 index 0000000..70a2193 --- /dev/null +++ b/src/Entities/Correction.php @@ -0,0 +1,215 @@ +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; + } + + /** + * @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; + } +} diff --git a/src/Entities/Payment.php b/src/Entities/Payment.php index 2982833..f10d671 100644 --- a/src/Entities/Payment.php +++ b/src/Entities/Payment.php @@ -13,20 +13,15 @@ namespace AtolOnline\Entities; use AtolOnline\Constants\Constraints; use AtolOnline\Enums\PaymentTypes; -use AtolOnline\Exceptions\{ - InvalidEnumValueException, - NegativePaymentSumException, - TooHighPaymentSumException,}; -use JetBrains\PhpStorm\{ - ArrayShape, - Pure}; +use AtolOnline\Exceptions\{InvalidEnumValueException, NegativePaymentSumException, TooHighPaymentSumException,}; +use JetBrains\PhpStorm\{ArrayShape, Pure}; /** * Класс, описывающий оплату * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30 */ -class Payment extends Entity +final class Payment extends Entity { /** * @var int Тип оплаты diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index 632d634..07b40c5 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -26,7 +26,7 @@ use Exception; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ -class Receipt extends Entity +final class Receipt extends Entity { /** * @var Client Покупатель @@ -35,6 +35,7 @@ class Receipt extends Entity /** * @var Company Продавец + * @todo вынести в трейт */ protected Company $company; @@ -55,6 +56,7 @@ class Receipt extends Entity /** * @var Payments Коллекция оплат + * @todo вынести в трейт */ protected Payments $payments; @@ -70,6 +72,7 @@ class Receipt extends Entity /** * @var string|null ФИО кассира + * @todo вынести в трейт */ protected ?string $cashier = null; @@ -112,7 +115,7 @@ class Receipt extends Entity * Устанаваливает покупателя * * @param Client $client - * @return Receipt + * @return $this */ public function setClient(Client $client): self { @@ -134,7 +137,7 @@ class Receipt extends Entity * Устанаваливает продавца * * @param Company $company - * @return Receipt + * @return $this */ public function setCompany(Company $company): self { @@ -156,7 +159,7 @@ class Receipt extends Entity * Устанаваливает агента * * @param AgentInfo|null $agent_info - * @return Receipt + * @return $this */ public function setAgentInfo(?AgentInfo $agent_info): self { @@ -178,7 +181,7 @@ class Receipt extends Entity * Поставщика * * @param Supplier|null $supplier - * @return Receipt + * @return $this */ public function setSupplier(?Supplier $supplier): self { @@ -199,12 +202,12 @@ class Receipt extends Entity /** * Устанаваливает коллекцию предметов расчёта * - * @todo исключение при пустой коллекции * @param Items $items - * @return Receipt + * @return $this * @throws EmptyItemsException * @throws InvalidEntityInCollectionException * @throws Exception + * @todo исключение при пустой коллекции */ public function setItems(Items $items): self { @@ -230,7 +233,7 @@ class Receipt extends Entity * Устанаваливает коллекцию оплат * * @param Payments $payments - * @return Receipt + * @return $this * @throws InvalidEntityInCollectionException */ public function setPayments(Payments $payments): self @@ -255,7 +258,7 @@ class Receipt extends Entity * Устанаваливает коллекцию ставок НДС * * @param Vats|null $vats - * @return Receipt + * @return $this * @throws Exception */ public function setVats(?Vats $vats): self @@ -292,7 +295,7 @@ class Receipt extends Entity * Устанаваливает кассира * * @param string|null $cashier - * @return Receipt + * @return $this * @throws TooLongCashierException */ public function setCashier(?string $cashier): self @@ -321,7 +324,7 @@ class Receipt extends Entity * Устанаваливает дополнительный реквизит чека * * @param string|null $add_check_props - * @return Receipt + * @return $this * @throws TooLongAddCheckPropException */ public function setAddCheckProps(?string $add_check_props): self @@ -350,7 +353,7 @@ class Receipt extends Entity * Устанаваливает дополнительный реквизит пользователя * * @param AdditionalUserProps|null $add_user_props - * @return Receipt + * @return $this */ public function setAddUserProps(?AdditionalUserProps $add_user_props): self { diff --git a/src/Enums/DocumentTypes.php b/src/Enums/DocumentTypes.php index 706a0d3..8cf0869 100644 --- a/src/Enums/DocumentTypes.php +++ b/src/Enums/DocumentTypes.php @@ -17,12 +17,12 @@ namespace AtolOnline\Enums; final class DocumentTypes extends Enum { /** - * Чек прихода, возврата прихода, расхода, возврата расхода + * Документ прихода, возврата прихода, расхода, возврата расхода */ const RECEIPT = 'receipt'; /** - * Чек коррекции + * Документ коррекции */ const CORRECTION = 'correction'; diff --git a/tests/AtolOnline/Tests/Entities/CorrectionTest.php b/tests/AtolOnline/Tests/Entities/CorrectionTest.php new file mode 100644 index 0000000..759cde4 --- /dev/null +++ b/tests/AtolOnline/Tests/Entities/CorrectionTest.php @@ -0,0 +1,138 @@ +newCorrection(); + $this->assertIsAtolable($correction); + } + + /** + * Тестирует установку валидного кассира + * + * @return void + * @covers \AtolOnline\Entities\Correction::setCashier + * @covers \AtolOnline\Entities\Correction::getCashier + * @covers \AtolOnline\Entities\Correction::jsonSerialize + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws TooLongCashierException + */ + public function testCashier(): void + { + $correction = $this->newCorrection()->setCashier(Helpers::randomStr()); + $this->assertArrayHasKey('cashier', $correction->jsonSerialize()); + $this->assertEquals($correction->getCashier(), $correction->jsonSerialize()['cashier']); + } + + /** + * Тестирует обнуление кассира + * + * @param mixed $param + * @return void + * @dataProvider providerNullableStrings + * @covers \AtolOnline\Entities\Correction::setCashier + * @covers \AtolOnline\Entities\Correction::getCashier + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws TooLongCashierException + */ + public function testNullableCashier(mixed $param): void + { + $this->assertNull($this->newCorrection()->setCashier($param)->getCashier()); + } + + /** + * Тестирует выброс исключения при установке слишком длинного кассира (лол) + * + * @return void + * @covers \AtolOnline\Entities\Correction::setCashier + * @covers \AtolOnline\Exceptions\TooLongCashierException + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws TooLongCashierException + */ + public function testTooLongCashierException(): void + { + $this->expectException(TooLongCashierException::class); + $this->newCorrection()->setCashier(Helpers::randomStr(Constraints::MAX_LENGTH_CASHIER_NAME + 1)); + } + + /** + * Возвращает валидный тестовый объект чека + * + * @return Correction + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + */ + protected function newCorrection(): Correction + { + return new Correction( + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', Helpers::randomStr()), + new Payments($this->generatePaymentObjects(2)), + new Vats($this->generateVatObjects(2)), + ); + } +} From b4af189292034075add17e074c335e05356ac8d8 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 11 Dec 2021 15:54:42 +0800 Subject: [PATCH 73/85] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- composer.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index bdbbb02..4eda461 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,6 @@ }, "scripts": { "test": "vendor/bin/phpunit --colors=always", - "test-cov": "php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html .coverage-report" + "coverage": "php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html .coverage" } } diff --git a/composer.lock b/composer.lock index 9f68c2c..4d9eef4 100644 --- a/composer.lock +++ b/composer.lock @@ -1935,16 +1935,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "shasum": "" }, "require": { @@ -1996,9 +1996,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2021-12-08T12:19:24+00:00" }, { "name": "phpunit/php-code-coverage", From 294a3ef2f3af4e54e4d6dc59e0518a359b97da35 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 12 Dec 2021 11:09:12 +0800 Subject: [PATCH 74/85] =?UTF-8?q?=D0=94=D0=BE=D0=BF=D0=BE=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=D0=B8=D0=B5=20`ArrayAccess`-=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/Entities/Entity.php | 7 +- tests/AtolOnline/Tests/BasicTestCase.php | 3 + .../AtolOnline/Tests/Entities/ClientTest.php | 76 +++++++++++++++---- 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index c7c344d..385ee04 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ | Протокол | API | ФФД | Статус | |----------|-----|------|-------------| -| v4 | 5.7 | 1.05 | Рефакторинг | +| v4 | 5.8 | 1.05 | Рефакторинг | | v5 | 2.0 | 1.2 | В планах | ## Плюшечки diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index e0eee84..82eb1fc 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -7,11 +7,12 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -declare(strict_types = 1); +declare(strict_types=1); namespace AtolOnline\Entities; use ArrayAccess; +use BadMethodCallException; use Illuminate\Contracts\Support\Arrayable; use JsonSerializable; use Stringable; @@ -65,7 +66,7 @@ abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayA */ public function offsetSet(mixed $offset, mixed $value) { - throw new \BadMethodCallException( + throw new BadMethodCallException( 'Объект ' . static::class . ' нельзя изменять как массив. Следует использовать сеттеры.' ); } @@ -75,7 +76,7 @@ abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayA */ public function offsetUnset(mixed $offset): void { - throw new \BadMethodCallException( + throw new BadMethodCallException( 'Объект ' . static::class . ' нельзя изменять как массив. Следует использовать сеттеры.' ); } diff --git a/tests/AtolOnline/Tests/BasicTestCase.php b/tests/AtolOnline/Tests/BasicTestCase.php index 8c6aacf..22f1a1b 100644 --- a/tests/AtolOnline/Tests/BasicTestCase.php +++ b/tests/AtolOnline/Tests/BasicTestCase.php @@ -93,6 +93,7 @@ class BasicTestCase extends TestCase * @param Entity|EntityCollection $entity * @param array|null $json_structure * @covers \AtolOnline\Entities\Entity::__toString + * @covers \AtolOnline\Entities\Entity::toArray * @covers \AtolOnline\Entities\Entity::jsonSerialize * @covers \AtolOnline\Collections\EntityCollection::jsonSerialize * @throws Exception @@ -100,6 +101,8 @@ class BasicTestCase extends TestCase public function assertIsAtolable(Entity|EntityCollection $entity, ?array $json_structure = null): void { $this->assertIsArray($entity->jsonSerialize()); + $this->assertIsArray($entity->toArray()); + $this->assertEquals($entity->jsonSerialize(), $entity->toArray()); $this->assertIsString((string)$entity); $this->assertJson((string)$entity); if (!is_null($json_structure)) { diff --git a/tests/AtolOnline/Tests/Entities/ClientTest.php b/tests/AtolOnline/Tests/Entities/ClientTest.php index 25c8976..b68c8c3 100644 --- a/tests/AtolOnline/Tests/Entities/ClientTest.php +++ b/tests/AtolOnline/Tests/Entities/ClientTest.php @@ -9,15 +9,16 @@ namespace AtolOnline\Tests\Entities; -use AtolOnline\{ - Entities\Client, +use AtolOnline\{Entities\Client, Exceptions\InvalidEmailException, Exceptions\InvalidInnLengthException, Exceptions\InvalidPhoneException, Exceptions\TooLongClientNameException, Exceptions\TooLongEmailException, Helpers, - Tests\BasicTestCase}; + Tests\BasicTestCase +}; +use BadMethodCallException; use Exception; /** @@ -58,17 +59,19 @@ class ClientTest extends BasicTestCase $this->assertIsAtolable(new Client(email: 'john@example.com'), ['email' => 'john@example.com']); $this->assertIsAtolable(new Client(phone: '+1/22/99*73s dsdas654 5s6'), ['phone' => '+122997365456']); $this->assertIsAtolable(new Client(inn: '+fasd3\qe3fs_=nac99013928czc'), ['inn' => '3399013928']); - $this->assertIsAtolable(new Client( - 'John Doe', - 'john@example.com', - '+1/22/99*73s dsdas654 5s6', // +122997365456 - '+fasd3\qe3fs_=nac99013928czc' // 3399013928 - ), [ - 'name' => 'John Doe', - 'email' => 'john@example.com', - 'phone' => '+122997365456', - 'inn' => '3399013928', - ]); + $this->assertIsAtolable( + new Client( + 'John Doe', + 'john@example.com', + '+1/22/99*73s dsdas654 5s6', // +122997365456 + '+fasd3\qe3fs_=nac99013928czc' // 3399013928 + ), [ + 'name' => 'John Doe', + 'email' => 'john@example.com', + 'phone' => '+122997365456', + 'inn' => '3399013928', + ] + ); } /** @@ -131,12 +134,12 @@ class ClientTest extends BasicTestCase /** * Тестирует установку валидного телефона * + * @throws InvalidPhoneException * @todo актуализировать при доработатанной валидации * @dataProvider providerValidPhones * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Entities\Client::getPhone - * @throws InvalidPhoneException */ public function testValidPhone(string $input, string $output): void { @@ -146,11 +149,11 @@ class ClientTest extends BasicTestCase /** * Тестирует установку невалидного телефона * + * @throws InvalidPhoneException * @todo актуализировать при доработатанной валидации * @covers \AtolOnline\Entities\Client * @covers \AtolOnline\Entities\Client::setPhone * @covers \AtolOnline\Exceptions\InvalidPhoneException - * @throws InvalidPhoneException */ public function testInvalidPhoneException(): void { @@ -247,4 +250,45 @@ class ClientTest extends BasicTestCase $this->expectException(InvalidInnLengthException::class); (new Client())->setInn('1234567890123'); } + + /** + * Тестирует обращение к атрибутам объекта как к элементам массива + * + * @covers \AtolOnline\Entities\Entity::offsetGet + * @covers \AtolOnline\Entities\Entity::offsetExists + * @return void + */ + public function testOffsetGetExists(): void + { + $client = new Client('John Doe'); + $this->assertEquals('John Doe', $client['name']); + $this->assertTrue(isset($client['name'])); + $this->assertFalse(isset($client['qwerty'])); + } + + /** + * Тестирует выброс исключения при попытке задать значение атрибуту объекта как элементу массива + * + * @covers \AtolOnline\Entities\Entity::offsetSet + * @return void + */ + public function testBadMethodCallExceptionBySet(): void + { + $this->expectException(BadMethodCallException::class); + $client = new Client('John Doe'); + $client['name'] = 'qwerty'; + } + + /** + * Тестирует выброс исключения при попытке удалить значение атрибута объекта как элемент массива + * + * @covers \AtolOnline\Entities\Entity::offsetUnset + * @return void + */ + public function testBadMethodCallExceptionByUnset(): void + { + $this->expectException(BadMethodCallException::class); + $client = new Client('John Doe'); + unset($client['name']); + } } From 464a8f0706cd47a4713f8b13ea0a2fbbd224dfcb Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 12 Dec 2021 14:36:04 +0800 Subject: [PATCH 75/85] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D0=B5=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D1=8B=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=8F=D0=B4=D0=BE=D0=BA=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpunit.xml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 0c0f959..c851dd7 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,8 +12,17 @@ bootstrap="vendor/autoload.php" colors="true"> - - ./tests + + tests/AtolOnline/Tests/HelpersTest.php + + + tests/AtolOnline/Tests/Entities + + + tests/AtolOnline/Tests/Collections + + + tests/AtolOnline/Tests/Api From c7d07a18f10b017169668d4c3b3a3f27640edefb Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 12 Dec 2021 14:50:29 +0800 Subject: [PATCH 76/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D1=8B=20=D0=BF=D0=BE=20=D0=BA=D0=BE=D0=B4?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=B9=D0=BB=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/AtolClient.php | 34 ++++++++++------- src/Api/KktMonitor.php | 9 ++++- src/Api/KktResponse.php | 37 +++++++++++++------ src/Constants/Constraints.php | 29 ++++++++++++--- src/Entities/AdditionalUserProps.php | 9 ++++- src/Entities/Company.php | 10 ++++- src/Entities/Correction.php | 24 +++++++----- src/Entities/Entity.php | 4 +- src/Entities/Item.php | 4 +- src/Entities/Payment.php | 17 +++++++-- src/Entities/Receipt.php | 9 ++--- src/Entities/Vat.php | 20 +++++----- src/Enums/PaymentObjects.php | 1 + src/Enums/ReceiptOperationTypes.php | 2 +- src/Exceptions/AtolException.php | 2 + src/Exceptions/AuthFailedException.php | 2 + src/Exceptions/EmptyLoginException.php | 1 + src/Exceptions/EmptyPasswordException.php | 1 + .../InvalidPaymentAddressException.php | 4 +- src/Exceptions/InvalidUuidException.php | 12 +++--- src/TestEnvParams.php | 22 +++++++++++ 21 files changed, 178 insertions(+), 75 deletions(-) diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index ca3bf78..9b63445 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -11,15 +11,19 @@ declare(strict_types = 1); namespace AtolOnline\Api; -use AtolOnline\{ - Constants\Constraints, - Exceptions\AuthFailedException, - Exceptions\EmptyLoginException, - Exceptions\EmptyPasswordException, - Exceptions\TooLongLoginException, - Exceptions\TooLongPasswordException}; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\GuzzleException; +use AtolOnline\Constants\Constraints; +use AtolOnline\Exceptions\{ + AuthFailedException, + EmptyLoginException, + EmptyPasswordException, + TooLongLoginException, + TooLongPasswordException +}; +use GuzzleHttp\{ + Client, + Exception\GuzzleException +}; +use JetBrains\PhpStorm\Pure; /** * Класс для подключения АТОЛ Онлайн API @@ -74,10 +78,13 @@ abstract class AtolClient ?string $login = null, ?string $password = null, array $config = [] - ) { - $this->http = new Client(array_merge($config, [ - 'http_errors' => $config['http_errors'] ?? false, - ])); + ) + { + $this->http = new Client( + array_merge($config, [ + 'http_errors' => $config['http_errors'] ?? false, + ]) + ); $this->setTestMode($test_mode); !is_null($login) && $this->setLogin($login); !is_null($password) && $this->setPassword($password); @@ -201,6 +208,7 @@ abstract class AtolClient * * @return array */ + #[Pure] private function getHeaders(): array { $headers['Content-type'] = 'application/json; charset=utf-8'; diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index e43f43c..a2989f0 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -12,10 +12,13 @@ declare(strict_types = 1); namespace AtolOnline\Api; use AtolOnline\Entities\Kkt; -use AtolOnline\Exceptions\EmptyMonitorDataException; -use AtolOnline\Exceptions\NotEnoughMonitorDataException; +use AtolOnline\Exceptions\{ + EmptyMonitorDataException, + NotEnoughMonitorDataException +}; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Collection; +use JetBrains\PhpStorm\Pure; /** * Класс для мониторинга ККТ @@ -27,6 +30,7 @@ class KktMonitor extends AtolClient /** * @inheritDoc */ + #[Pure] protected function getAuthEndpoint(): string { return $this->isTestMode() @@ -37,6 +41,7 @@ class KktMonitor extends AtolClient /** * @inheritDoc */ + #[Pure] protected function getMainEndpoint(): string { return $this->isTestMode() diff --git a/src/Api/KktResponse.php b/src/Api/KktResponse.php index 796df1a..78988af 100644 --- a/src/Api/KktResponse.php +++ b/src/Api/KktResponse.php @@ -7,13 +7,18 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ +/** @noinspection PhpMultipleClassDeclarationsInspection */ + declare(strict_types = 1); namespace AtolOnline\Api; +use JetBrains\PhpStorm\{ + ArrayShape, + Pure +}; use JsonSerializable; use Psr\Http\Message\ResponseInterface; -use stdClass; use Stringable; /** @@ -30,10 +35,10 @@ class KktResponse implements JsonSerializable, Stringable protected int $code; /** - * @var stdClass|array|null Содержимое ответа сервера + * @var object|array|null Содержимое ответа сервера */ - protected stdClass|array|null $content; - + protected object|array|null $content; + /** * @var array Заголовки ответа */ @@ -50,7 +55,7 @@ class KktResponse implements JsonSerializable, Stringable $this->headers = $response->getHeaders(); $this->content = json_decode((string)$response->getBody()); } - + /** * Возвращает заголовки ответа * @@ -60,18 +65,19 @@ class KktResponse implements JsonSerializable, Stringable { return $this->headers; } - + /** * Возвращает запрошенный параметр из декодированного объекта результата * * @param $name * @return mixed */ + #[Pure] public function __get($name): mixed { return $this->getContent()?->$name; } - + /** * Возвращает код ответа * @@ -81,7 +87,7 @@ class KktResponse implements JsonSerializable, Stringable { return $this->code; } - + /** * Возвращает объект результата запроса * @@ -91,12 +97,13 @@ class KktResponse implements JsonSerializable, Stringable { return $this->content; } - + /** * Проверяет успешность запроса по соержимому результата * * @return bool */ + #[Pure] public function isValid(): bool { return !empty($this->getCode()) @@ -104,7 +111,7 @@ class KktResponse implements JsonSerializable, Stringable && empty($this->getContent()->error) && $this->getCode() < 400; } - + /** * Возвращает текстовое представление */ @@ -112,10 +119,16 @@ class KktResponse implements JsonSerializable, Stringable { return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE); } - + /** * @inheritDoc */ + #[ArrayShape([ + 'code' => 'int', + 'headers' => 'array|\string[][]', + 'body' => 'mixed', + ] + )] public function jsonSerialize(): array { return [ @@ -124,4 +137,4 @@ class KktResponse implements JsonSerializable, Stringable 'body' => $this->content, ]; } -} \ No newline at end of file +} diff --git a/src/Constants/Constraints.php b/src/Constants/Constraints.php index ea2a905..6b03802 100644 --- a/src/Constants/Constraints.php +++ b/src/Constants/Constraints.php @@ -43,24 +43,28 @@ final class Constraints /** * Максимальная длина наименования покупателя (1227) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ const MAX_LENGTH_CLIENT_NAME = 256; /** * Максимальная длина наименования предмета расчёта (1030) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_LENGTH_ITEM_NAME = 128; /** * Максимальная цена за единицу предмета расчёта (1079) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_COUNT_ITEM_PRICE = 42949672.95; /** * Максимальное количество (вес) единицы предмета расчёта (1023) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_COUNT_ITEM_QUANTITY = 99999.999; @@ -68,36 +72,42 @@ final class Constraints /** * Максимальная стоимость всех предметов расчёта в документе прихода, расхода, * возврата прихода, возврата расхода (1043) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_COUNT_ITEM_SUM = 42949672.95; /** * Максимальная длина телефона или email покупателя (1008) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 17 */ const MAX_LENGTH_CLIENT_CONTACT = 64; /** * Длина операции для платёжного агента (1044) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 19 */ const MAX_LENGTH_PAYING_AGENT_OPERATION = 24; /** * Максимальное количество предметов расчёта в документе прихода, расхода, возврата прихода, возврата расхода + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_COUNT_DOC_ITEMS = 100; /** * Максимальная длина единицы измерения предмета расчётов + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 21 */ const MAX_LENGTH_MEASUREMENT_UNIT = 16; /** * Максимальная длина пользовательских данных для предмета расчётов (1191) + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 29 */ const MAX_LENGTH_USER_DATA = 64; @@ -118,12 +128,14 @@ final class Constraints /** * Максимальное количество платежей в любом документе + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 30 и 35 */ const MAX_COUNT_DOC_PAYMENTS = 10; /** * Максимальное количество ставок НДС в любом документе + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 31 и 36 */ const MAX_COUNT_DOC_VATS = 6; @@ -178,26 +190,31 @@ final class Constraints * * @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/receipt/client/inn" */ - const PATTERN_INN = /* @lang PhpRegExp */ + const PATTERN_INN + = /* @lang PhpRegExp */ '/(^[\d]{10}$)|(^[\d]{12}$)/'; /** * Регулярное выражение для валидации номера телефона + * * @see https://online.atol.ru/possystem/v4/schema/sell Схема "#/definitions/phone_number" */ - const PATTERN_PHONE = /* @lang PhpRegExp */ + const PATTERN_PHONE + = /* @lang PhpRegExp */ '/^([^\s\\\]{0,17}|\+[^\s\\\]{1,18})$/'; /** * Регулярное выражение для валидации строки Callback URL */ - const PATTERN_CALLBACK_URL = /* @lang PhpRegExp */ - '/^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]' . - '([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/'; + const PATTERN_CALLBACK_URL + = /* @lang PhpRegExp */ + '/^http(s?):\/\/[0-9a-zA-Zа-яА-Я]' . + '([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-.?,\'\/\\\+&=%\$#_]*)?$/'; /** * Регулярное выражение для валидации кода страны происхождения товара */ - const PATTERN_OKSM_CODE = /* @lang PhpRegExp */ + const PATTERN_OKSM_CODE + = /* @lang PhpRegExp */ '/^[\d]{3}$/'; } diff --git a/src/Entities/AdditionalUserProps.php b/src/Entities/AdditionalUserProps.php index bec5123..57e3aa0 100644 --- a/src/Entities/AdditionalUserProps.php +++ b/src/Entities/AdditionalUserProps.php @@ -16,8 +16,12 @@ use AtolOnline\Exceptions\{ EmptyAddUserPropNameException, EmptyAddUserPropValueException, TooLongAddUserPropNameException, - TooLongAddUserPropValueException}; -use JetBrains\PhpStorm\Pure; + TooLongAddUserPropValueException +}; +use JetBrains\PhpStorm\{ + ArrayShape, + Pure +}; /** * Класс, описывающий дополнительный реквизит пользователя @@ -117,6 +121,7 @@ final class AdditionalUserProps extends Entity * @inheritDoc */ #[Pure] + #[ArrayShape(['name' => 'string', 'value' => 'null|string'])] public function jsonSerialize(): array { return [ diff --git a/src/Entities/Company.php b/src/Entities/Company.php index ed665ca..e5b4b59 100644 --- a/src/Entities/Company.php +++ b/src/Entities/Company.php @@ -11,8 +11,14 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\{Constants\Constraints, Enums\SnoTypes, Traits\HasEmail, Traits\HasInn}; -use AtolOnline\Exceptions\{InvalidEmailException, +use AtolOnline\{ + Constants\Constraints, + Enums\SnoTypes, + Traits\HasEmail, + Traits\HasInn +}; +use AtolOnline\Exceptions\{ + InvalidEmailException, InvalidEnumValueException, InvalidInnLengthException, InvalidPaymentAddressException, diff --git a/src/Entities/Correction.php b/src/Entities/Correction.php index 70a2193..2a8aea5 100644 --- a/src/Entities/Correction.php +++ b/src/Entities/Correction.php @@ -9,9 +9,15 @@ namespace AtolOnline\Entities; -use AtolOnline\Collections\{Payments, Vats}; -use AtolOnline\Constants\Constraints; -use AtolOnline\Exceptions\{InvalidEntityInCollectionException, TooLongCashierException}; +use AtolOnline\{ + Constants\Constraints, + Payments, + Vats +}; +use AtolOnline\Exceptions\{ + InvalidEntityInCollectionException, + TooLongCashierException +}; use Exception; use JetBrains\PhpStorm\ArrayShape; @@ -28,8 +34,8 @@ class Correction extends Entity protected Company $company; /** + * @todo вынести в трейт? * @var string|null ФИО кассира - * @todo вынести в трейт */ protected ?string $cashier = null; @@ -195,11 +201,11 @@ class Correction extends Entity * @throws InvalidEntityInCollectionException */ #[ArrayShape([ - 'company' => "\AtolOnline\Entities\Company", - 'correction_info' => "\AtolOnline\Entities\CorrectionInfo", - 'payments' => "array", - 'vats' => "\AtolOnline\Collections\Vats|null", - 'cashier' => "null|string" + 'company' => '\AtolOnline\Entities\Company', + 'correction_info' => '\AtolOnline\Entities\CorrectionInfo', + 'payments' => 'array', + 'vats' => '\AtolOnline\Collections\Vats|null', + 'cashier' => 'null|string', ])] public function jsonSerialize(): array { diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index 82eb1fc..24c524c 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -7,7 +7,9 @@ * https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE */ -declare(strict_types=1); +/** @noinspection PhpMultipleClassDeclarationsInspection */ + +declare(strict_types = 1); namespace AtolOnline\Entities; diff --git a/src/Entities/Item.php b/src/Entities/Item.php index e82bd7c..d994a02 100644 --- a/src/Entities/Item.php +++ b/src/Entities/Item.php @@ -556,8 +556,8 @@ final class Item extends Entity if (is_string($declaration_number)) { $declaration_number = trim($declaration_number); if ( - mb_strlen($declaration_number) < Constraints::MIN_LENGTH_DECLARATION_NUMBER || - mb_strlen($declaration_number) > Constraints::MAX_LENGTH_DECLARATION_NUMBER + mb_strlen($declaration_number) < Constraints::MIN_LENGTH_DECLARATION_NUMBER + || mb_strlen($declaration_number) > Constraints::MAX_LENGTH_DECLARATION_NUMBER ) { throw new InvalidDeclarationNumberException($declaration_number); } diff --git a/src/Entities/Payment.php b/src/Entities/Payment.php index f10d671..e4e57b4 100644 --- a/src/Entities/Payment.php +++ b/src/Entities/Payment.php @@ -11,10 +11,19 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Constants\Constraints; -use AtolOnline\Enums\PaymentTypes; -use AtolOnline\Exceptions\{InvalidEnumValueException, NegativePaymentSumException, TooHighPaymentSumException,}; -use JetBrains\PhpStorm\{ArrayShape, Pure}; +use AtolOnline\{ + Constants\Constraints, + Enums\PaymentTypes, +}; +use AtolOnline\Exceptions\{ + InvalidEnumValueException, + NegativePaymentSumException, + TooHighPaymentSumException, +}; +use JetBrains\PhpStorm\{ + ArrayShape, + Pure +}; /** * Класс, описывающий оплату diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index 07b40c5..ee5183b 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -34,8 +34,8 @@ final class Receipt extends Entity protected Client $client; /** + * @todo вынести в трейт? * @var Company Продавец - * @todo вынести в трейт */ protected Company $company; @@ -55,8 +55,8 @@ final class Receipt extends Entity protected Items $items; /** + * @todo вынести в трейт? * @var Payments Коллекция оплат - * @todo вынести в трейт */ protected Payments $payments; @@ -71,8 +71,8 @@ final class Receipt extends Entity protected float $total = 0; /** + * @todo вынести в трейт? * @var string|null ФИО кассира - * @todo вынести в трейт */ protected ?string $cashier = null; @@ -204,10 +204,9 @@ final class Receipt extends Entity * * @param Items $items * @return $this - * @throws EmptyItemsException * @throws InvalidEntityInCollectionException * @throws Exception - * @todo исключение при пустой коллекции + * @throws EmptyItemsException */ public function setItems(Items $items): self { diff --git a/src/Entities/Vat.php b/src/Entities/Vat.php index 5b16069..e65e829 100644 --- a/src/Entities/Vat.php +++ b/src/Entities/Vat.php @@ -106,15 +106,17 @@ final class Vat extends Entity #[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, - }); + 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, + } + ); } /** diff --git a/src/Enums/PaymentObjects.php b/src/Enums/PaymentObjects.php index eee6f8c..c5ac2f0 100644 --- a/src/Enums/PaymentObjects.php +++ b/src/Enums/PaymentObjects.php @@ -77,6 +77,7 @@ final class PaymentObjects extends Enum /** * Составной предмет расчёта + * * @deprecated Более не используется согласно ФФД 1.05 * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 25 (payment_object) */ diff --git a/src/Enums/ReceiptOperationTypes.php b/src/Enums/ReceiptOperationTypes.php index 2e55820..ad342c5 100644 --- a/src/Enums/ReceiptOperationTypes.php +++ b/src/Enums/ReceiptOperationTypes.php @@ -47,4 +47,4 @@ final class ReceiptOperationTypes extends Enum * Коррекция прихода (догоняем неучтённые средства) */ const BUY_CORRECTION = 'buy_correction'; -} \ No newline at end of file +} diff --git a/src/Exceptions/AtolException.php b/src/Exceptions/AtolException.php index 6a7f34e..199cb5d 100644 --- a/src/Exceptions/AtolException.php +++ b/src/Exceptions/AtolException.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; use Exception; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при работе с АТОЛ Онлайн @@ -29,6 +30,7 @@ class AtolException extends Exception * @param string $message Сообщение * @param int[] $ffd_tags Переопредление тегов ФФД */ + #[Pure] public function __construct(string $message = '', array $ffd_tags = []) { $tags = implode(', ', $ffd_tags ?: $this->ffd_tags); diff --git a/src/Exceptions/AuthFailedException.php b/src/Exceptions/AuthFailedException.php index 0d379b0..61be470 100644 --- a/src/Exceptions/AuthFailedException.php +++ b/src/Exceptions/AuthFailedException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; use AtolOnline\Api\KktResponse; use Exception; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при неудачной авторизации @@ -25,6 +26,7 @@ class AuthFailedException extends Exception * @param KktResponse $response * @param string $message */ + #[Pure] public function __construct(KktResponse $response, string $message = '') { parent::__construct(($message ?: 'Ошибка авторизации: ') . ': ' . $response); diff --git a/src/Exceptions/EmptyLoginException.php b/src/Exceptions/EmptyLoginException.php index 5e78966..49e4ff9 100644 --- a/src/Exceptions/EmptyLoginException.php +++ b/src/Exceptions/EmptyLoginException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать пустой логин + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12 */ class EmptyLoginException extends AtolException diff --git a/src/Exceptions/EmptyPasswordException.php b/src/Exceptions/EmptyPasswordException.php index 76f4a9d..e533229 100644 --- a/src/Exceptions/EmptyPasswordException.php +++ b/src/Exceptions/EmptyPasswordException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; /** * Исключение, возникающее при попытке указать пустой пароль + * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 12 */ class EmptyPasswordException extends AtolException diff --git a/src/Exceptions/InvalidPaymentAddressException.php b/src/Exceptions/InvalidPaymentAddressException.php index 59a0bef..7fb8af0 100644 --- a/src/Exceptions/InvalidPaymentAddressException.php +++ b/src/Exceptions/InvalidPaymentAddressException.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; use AtolOnline\Constants\Ffd105Tags; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при попытке указать некорректный адрес места расчётов @@ -28,7 +29,8 @@ class InvalidPaymentAddressException extends AtolException * @param string $address * @param string $message */ - public function __construct($address = '', $message = "") + #[Pure] + public function __construct(string $address = '', string $message = '') { parent::__construct($message ?: "Некорректный адрес места расчётов: '$address'"); } diff --git a/src/Exceptions/InvalidUuidException.php b/src/Exceptions/InvalidUuidException.php index e96ed82..77fcba6 100644 --- a/src/Exceptions/InvalidUuidException.php +++ b/src/Exceptions/InvalidUuidException.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use JetBrains\PhpStorm\Pure; + /** * Исключение, возникающее при ошибке валидации UUID */ @@ -19,13 +21,11 @@ class InvalidUuidException extends AtolException /** * Конструктор * - * @param $uuid - * @param string $message - * @param int $code - * @param Throwable|null $previous + * @param string $uuid */ - public function __construct(?string $uuid = null) + #[Pure] + public function __construct(string $uuid = '') { - parent::__construct('Невалидный UUID' . ($uuid ? ': ' . $uuid : '')); + parent::__construct('Невалидный UUID: ' . $uuid); } } diff --git a/src/TestEnvParams.php b/src/TestEnvParams.php index 46e7733..c1745cd 100644 --- a/src/TestEnvParams.php +++ b/src/TestEnvParams.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline; +use JetBrains\PhpStorm\ArrayShape; + /** * Константы, определяющие параметры тестовых сред * @@ -23,6 +25,16 @@ final class TestEnvParams * * @return string[] */ + #[ArrayShape([ + 'endpoint' => "string", + 'company_name' => "string", + 'inn' => "string", + 'payment_address' => "string", + 'group' => "string", + 'login' => "string", + 'password' => "string", + 'endpoint_ofd' => "string", + ])] public static function FFD105(): array { return [ @@ -43,6 +55,16 @@ final class TestEnvParams * @return string[] * @noinspection PhpUnused */ + #[ArrayShape([ + 'endpoint' => "string", + 'company_name' => "string", + 'inn' => "string", + 'payment_address' => "string", + 'group' => "string", + 'login' => "string", + 'password' => "string", + 'endpoint_ofd' => "string", + ])] public static function FFD12(): array { return [ From 19653776c596b24fc546092a7e1c49d2b97f3af7 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 16 Dec 2021 18:33:13 +0800 Subject: [PATCH 77/85] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=BB=D0=B0=D1=82=20=D0=B8=20=D1=81=D1=82=D0=B0=D0=B2=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BA?= =?UTF-8?q?=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Entities/Correction.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Entities/Correction.php b/src/Entities/Correction.php index 2a8aea5..30a2736 100644 --- a/src/Entities/Correction.php +++ b/src/Entities/Correction.php @@ -10,14 +10,12 @@ namespace AtolOnline\Entities; use AtolOnline\{ - Constants\Constraints, - Payments, - Vats -}; + Collections\Payments, + Collections\Vats, + Constants\Constraints}; use AtolOnline\Exceptions\{ InvalidEntityInCollectionException, - TooLongCashierException -}; + TooLongCashierException}; use Exception; use JetBrains\PhpStorm\ArrayShape; From 573af15bac0ad0da9ec7a0e2ee4aede125f52c1d Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Thu, 16 Dec 2021 18:35:49 +0800 Subject: [PATCH 78/85] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=D1=8B=20=D0=B3?= =?UTF-8?q?=D0=B5=D1=82=D1=82=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BB=D0=BE=D0=B3?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0=20=D0=B8=20=D0=BF=D0=B0=D1=80=D0=BE=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20`AtolClient`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/AtolClient.php | 26 +++++++++++-------- tests/AtolOnline/Tests/Api/KktMonitorTest.php | 7 +++-- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index 9b63445..6bae018 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -17,12 +17,11 @@ use AtolOnline\Exceptions\{ EmptyLoginException, EmptyPasswordException, TooLongLoginException, - TooLongPasswordException -}; + TooLongPasswordException}; +use AtolOnline\TestEnvParams; use GuzzleHttp\{ Client, - Exception\GuzzleException -}; + Exception\GuzzleException}; use JetBrains\PhpStorm\Pure; /** @@ -78,8 +77,7 @@ abstract class AtolClient ?string $login = null, ?string $password = null, array $config = [] - ) - { + ) { $this->http = new Client( array_merge($config, [ 'http_errors' => $config['http_errors'] ?? false, @@ -149,9 +147,12 @@ abstract class AtolClient * * @return string|null */ + #[Pure] public function getLogin(): ?string { - return $this->login; + return $this->isTestMode() + ? TestEnvParams::FFD105()['login'] + : $this->login; } /** @@ -179,9 +180,12 @@ abstract class AtolClient * * @return string|null */ + #[Pure] public function getPassword(): ?string { - return $this->password; + return $this->isTestMode() + ? TestEnvParams::FFD105()['password'] + : $this->password; } /** @@ -279,7 +283,7 @@ abstract class AtolClient /** * Выполняет авторизацию на сервере АТОЛ * - * Авторизация выолнится только если неизвестен токен + * Авторизация выполнится только если неизвестен токен * * @param string|null $login * @param string|null $password @@ -294,8 +298,8 @@ abstract class AtolClient public function auth(?string $login = null, ?string $password = null): bool { if (empty($this->getToken())) { - $login && $this->setLogin($login); - $password && $this->setPassword($password); + !is_null($login) && $this->setLogin($login); + !is_null($password) && $this->setPassword($password); if ($token = $this->doAuth()) { $this->setToken($token); } diff --git a/tests/AtolOnline/Tests/Api/KktMonitorTest.php b/tests/AtolOnline/Tests/Api/KktMonitorTest.php index 34ccb7f..af0836b 100644 --- a/tests/AtolOnline/Tests/Api/KktMonitorTest.php +++ b/tests/AtolOnline/Tests/Api/KktMonitorTest.php @@ -32,7 +32,7 @@ use GuzzleHttp\Exception\GuzzleException; class KktMonitorTest extends BasicTestCase { /** - * Возвращает объект клиента для тестирования с тестовым API АТОЛ + * Возвращает объект монитора для тестирования с тестовым API АТОЛ * * @return KktMonitor * @throws EmptyLoginException @@ -42,10 +42,9 @@ class KktMonitorTest extends BasicTestCase */ private function newTestClient(): KktMonitor { - $credentials = TestEnvParams::FFD105(); return (new KktMonitor(true)) - ->setLogin($credentials['login']) - ->setPassword($credentials['password']); + ->setLogin(TestEnvParams::FFD105()['login']) + ->setPassword(TestEnvParams::FFD105()['password']); } /** From b4cc0fec534f5c650cb895c72379c73b52760aa6 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 18 Dec 2021 14:09:07 +0800 Subject: [PATCH 79/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BE=D1=87=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=20com?= =?UTF-8?q?poser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index 4eda461..96ff66e 100644 --- a/composer.json +++ b/composer.json @@ -75,5 +75,9 @@ "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 } } From 71d1f2900cedddfec463b8673fb8f4fe949ec460 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sat, 18 Dec 2021 14:45:00 +0800 Subject: [PATCH 80/85] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D1=84=D0=B8=D1=81=D0=BA=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - у `AtolClient` теперь возможно получить последний отправленный запрос `getLastRequest()` - у `AtolClient::auth()` удалены аргументы за ненадобностью - улучшен `Client::jsonSerialize()` - исправлен `Receipt::jsonSerialize()` - у `Receipt` и `Correction` появились методы фискализации, вкусный сахарок - удалён енам `DocumentTypes` за ненадобностью - исправлены тесты монитора и документов - рабочий фискализатор с получением результатов и покрытием --- src/Api/AtolClient.php | 69 +-- src/Api/KktFiscalizer.php | 346 +++++++-------- src/Api/KktMonitor.php | 20 +- src/Entities/Client.php | 8 +- src/Entities/Correction.php | 56 ++- src/Entities/Entity.php | 8 + src/Entities/Receipt.php | 105 ++++- src/Enums/DocumentTypes.php | 36 -- src/Exceptions/EmptyGroupException.php | 20 + .../InvalidCorrectionDateException.php | 2 + .../InvalidDeclarationNumberException.php | 2 + src/Exceptions/InvalidEmailException.php | 2 + src/Exceptions/InvalidInnLengthException.php | 2 + src/Exceptions/InvalidJsonException.php | 3 + src/Exceptions/InvalidOKSMCodeException.php | 2 + src/Exceptions/InvalidPhoneException.php | 2 + .../NegativeItemExciseException.php | 2 + src/Exceptions/NegativeItemPriceException.php | 2 + .../NegativeItemQuantityException.php | 2 + .../NegativePaymentSumException.php | 2 + .../NotEnoughMonitorDataException.php | 3 + src/Exceptions/TooHighItemPriceException.php | 2 + .../TooHighItemQuantityException.php | 2 + src/Exceptions/TooHighItemSumException.php | 2 + .../TooLongCallbackUrlException.php | 2 +- src/Exceptions/TooLongException.php | 3 + src/Exceptions/TooLongItemCodeException.php | 2 + src/Exceptions/TooManyException.php | 3 + .../Tests/Api/KktFiscalizerTest.php | 417 ++++++++++++++++++ tests/AtolOnline/Tests/Api/KktMonitorTest.php | 21 +- tests/AtolOnline/Tests/BasicTestCase.php | 65 ++- .../Tests/Entities/CorrectionTest.php | 35 +- .../AtolOnline/Tests/Entities/ReceiptTest.php | 57 +-- 33 files changed, 969 insertions(+), 336 deletions(-) delete mode 100644 src/Enums/DocumentTypes.php create mode 100644 src/Exceptions/EmptyGroupException.php create mode 100644 tests/AtolOnline/Tests/Api/KktFiscalizerTest.php diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index 6bae018..2c7b608 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -29,6 +29,16 @@ use JetBrains\PhpStorm\Pure; */ abstract class AtolClient { + /** + * @var array Последний запрос к серверу АТОЛ + */ + protected array $request; + + /** + * @var KktResponse|null Последний ответ сервера АТОЛ + */ + protected ?KktResponse $response; + /** * @var bool Флаг тестового режима */ @@ -54,11 +64,6 @@ abstract class AtolClient */ private ?string $token = null; - /** - * @var KktResponse|null Последний ответ сервера АТОЛ - */ - private ?KktResponse $response; - /** * Конструктор * @@ -88,6 +93,26 @@ abstract class AtolClient !is_null($password) && $this->setPassword($password); } + /** + * Возвращает последний запрос к серверу + * + * @return array + */ + public function getLastRequest(): array + { + return $this->request; + } + + /** + * Возвращает последний ответ сервера + * + * @return KktResponse|null + */ + public function getLastResponse(): ?KktResponse + { + return $this->response; + } + /** * Возвращает установленный флаг тестового режима * @@ -132,16 +157,6 @@ abstract class AtolClient return $this; } - /** - * Возвращает последний ответ сервера - * - * @return KktResponse|null - */ - public function getResponse(): ?KktResponse - { - return $this->response; - } - /** * Возвращает логин доступа к API * @@ -273,36 +288,29 @@ abstract class AtolClient ): KktResponse { $http_method = strtoupper(trim($http_method)); $options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []); - if ($http_method != 'GET') { - $options['json'] = $data; - } + $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 KktResponse($response); } /** * Выполняет авторизацию на сервере АТОЛ - * * Авторизация выполнится только если неизвестен токен * - * @param string|null $login - * @param string|null $password * @return bool * @throws AuthFailedException - * @throws TooLongLoginException * @throws EmptyLoginException * @throws EmptyPasswordException - * @throws TooLongPasswordException * @throws GuzzleException */ - public function auth(?string $login = null, ?string $password = null): bool + public function auth(): bool { - if (empty($this->getToken())) { - !is_null($login) && $this->setLogin($login); - !is_null($password) && $this->setPassword($password); - if ($token = $this->doAuth()) { - $this->setToken($token); - } + if (empty($this->getToken()) && $token = $this->doAuth()) { + $this->setToken($token); } return !empty($this->getToken()); } @@ -320,4 +328,5 @@ abstract class AtolClient * @return string */ abstract protected function getMainEndpoint(): string; + } diff --git a/src/Api/KktFiscalizer.php b/src/Api/KktFiscalizer.php index 47d43e3..6b593fb 100644 --- a/src/Api/KktFiscalizer.php +++ b/src/Api/KktFiscalizer.php @@ -13,30 +13,30 @@ namespace AtolOnline\Api; use AtolOnline\{ Constants\Constraints, - Entities\Company, - Entities\Document, - Exceptions\AuthFailedException, - Exceptions\EmptyCorrectionInfoException, - Exceptions\EmptyLoginException, - Exceptions\EmptyPasswordException, - Exceptions\InvalidCallbackUrlException, - Exceptions\InvalidDocumentTypeException, - Exceptions\InvalidInnLengthException, - Exceptions\InvalidUuidException, - Exceptions\TooLongCallbackUrlException, - Exceptions\TooLongLoginException, - Exceptions\TooLongPasswordException, - Exceptions\TooLongPaymentAddressException, - Exceptions\TooManyItemsException, - Exceptions\TooManyVatsException, - TestEnvParams -}; -use Exception; + 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; /** - * Класс для регистрации документов на ККТ + * Класс фискализатора для регистрации документов на ККТ */ class KktFiscalizer extends AtolClient { @@ -62,6 +62,7 @@ class KktFiscalizer extends AtolClient * @throws EmptyPasswordException * @throws TooLongLoginException * @throws TooLongPasswordException + * @throws EmptyGroupException * @see https://guzzle.readthedocs.io/en/latest/request-options.html */ public function __construct( @@ -75,211 +76,201 @@ class KktFiscalizer extends AtolClient !is_null($group) && $this->setGroup($group); } - /** - * Устанавливает группу доступа к ККТ - * - * @param string $group - * @return $this - */ - public function setGroup(string $group): self - { - // критерии к длине строки не описаны ни в схеме, ни в документации - $this->group = $group; - return $this; - } - /** * Возвращает группу доступа к ККТ в соответствии с флагом тестового режима * * @return string|null */ + #[Pure] public function getGroup(): ?string { - return $this->group; + return $this->isTestMode() + ? TestEnvParams::FFD105()['group'] + : $this->group; } /** - * Устанавливает URL для приёма колбеков + * Устанавливает группу доступа к ККТ * - * @param string $url + * @param string $group * @return $this - * @throws TooLongCallbackUrlException - * @throws InvalidCallbackUrlException + * @throws EmptyGroupException */ - public function setCallbackUrl(string $url): self + public function setGroup(string $group): self { - if (mb_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) { - throw new TooLongCallbackUrlException($url, Constraints::MAX_LENGTH_CALLBACK_URL); - } elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) { - throw new InvalidCallbackUrlException('Callback URL not matches with pattern'); - } - $this->callback_url = $url; + // критерии к длине строки не описаны ни в схеме, ни в документации + empty($group = trim($group)) && throw new EmptyGroupException(); + $this->group = $group; return $this; } /** * Возвращает URL для приёма колбеков * - * @return string + * @return string|null */ - public function getCallbackUrl(): string + 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 Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse + * @param Receipt $receipt Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null * @throws AuthFailedException - * @throws EmptyCorrectionInfoException - * @throws InvalidInnLengthException - * @throws TooLongPaymentAddressException - * @throws InvalidDocumentTypeException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ - public function sell(Document $document, ?string $external_id = null): KktResponse + public function sell(Receipt $receipt, ?string $external_id = null): ?KktResponse { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Некорректная операция над документом коррекции'); - } - return $this->registerDocument('sell', 'receipt', $document, $external_id); + return $this->registerDocument('sell', $receipt, $external_id); } /** * Регистрирует документ возврата прихода * - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse + * @param Receipt $receipt Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null * @throws AuthFailedException - * @throws EmptyCorrectionInfoException - * @throws InvalidInnLengthException - * @throws TooLongPaymentAddressException - * @throws TooManyVatsException - * @throws InvalidDocumentTypeException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ - public function sellRefund(Document $document, ?string $external_id = null): KktResponse + public function sellRefund(Receipt $receipt, ?string $external_id = null): ?KktResponse { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Invalid operation on correction document'); - } - return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id); + return $this->registerDocument('sell_refund', $receipt, $external_id); } /** * Регистрирует документ коррекции прихода * - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse + * @param Correction $correction Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null * @throws AuthFailedException - * @throws EmptyCorrectionInfoException - * @throws InvalidInnLengthException - * @throws TooLongPaymentAddressException - * @throws TooManyItemsException - * @throws InvalidDocumentTypeException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ - public function sellCorrection(Document $document, ?string $external_id = null): KktResponse + public function sellCorrect(Correction $correction, ?string $external_id = null): ?KktResponse { - if (!$document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException(); - } - $document->setClient(null)->setItems([]); - return $this->registerDocument('sell_correction', 'correction', $document, $external_id); + return $this->registerDocument('sell_correction', $correction, $external_id); } /** * Регистрирует документ расхода * - * @param Document $document - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse + * @param Receipt $receipt Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null * @throws AuthFailedException - * @throws EmptyCorrectionInfoException - * @throws InvalidInnLengthException - * @throws TooLongPaymentAddressException - * @throws InvalidDocumentTypeException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ - public function buy(Document $document, ?string $external_id = null): KktResponse + public function buy(Receipt $receipt, ?string $external_id = null): ?KktResponse { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Invalid operation on correction document'); - } - return $this->registerDocument('buy', 'receipt', $document, $external_id); + return $this->registerDocument('buy', $receipt, $external_id); } /** * Регистрирует документ возврата расхода * - * @param Document $document + * @param Receipt $receipt Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse + * @return KktResponse|null * @throws AuthFailedException - * @throws EmptyCorrectionInfoException - * @throws InvalidInnLengthException - * @throws TooLongPaymentAddressException - * @throws TooManyVatsException - * @throws InvalidDocumentTypeException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ - public function buyRefund(Document $document, ?string $external_id = null): KktResponse + public function buyRefund(Receipt $receipt, ?string $external_id = null): ?KktResponse { - if ($document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException('Invalid operation on correction document'); - } - return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id); + return $this->registerDocument('buy_refund', $receipt, $external_id); } /** * Регистрирует документ коррекции расхода * - * @param Document $document - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws EmptyCorrectionInfoException В документе отсутствуют данные коррекции - * @throws InvalidInnLengthException Некорректная длтина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов - * @throws TooManyItemsException Слишком много предметов расчёта - * @throws InvalidDocumentTypeException Некорректный тип документа + * @param Correction $correction Объект документа + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ - public function buyCorrection(Document $document, ?string $external_id = null): KktResponse + public function buyCorrect(Correction $correction, ?string $external_id = null): ?KktResponse { - if (!$document->getCorrectionInfo()) { - throw new EmptyCorrectionInfoException(); - } - $document->setClient(null)->setItems([]); - return $this->registerDocument('buy_correction', 'correction', $document, $external_id); + return $this->registerDocument('buy_correction', $correction, $external_id); } /** * Проверяет статус чека на ККТ один раз * * @param string $uuid UUID регистрации - * @return KktResponse + * @return KktResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws GuzzleException * @throws InvalidUuidException - * @throws TooLongLoginException - * @throws TooLongPasswordException */ - public function getDocumentStatus(string $uuid): KktResponse + public function getDocumentStatus(string $uuid): ?KktResponse { - $uuid = trim($uuid); - if (!Uuid::isValid($uuid)) { - throw new InvalidUuidException($uuid); - } - $this->auth(); - return $this->sendRequest('GET', 'report/' . $uuid); + !Uuid::isValid($uuid = trim($uuid)) && throw new InvalidUuidException($uuid); + return $this->auth() + ? $this->sendRequest('GET', $this->getFullUrl('report/' . $uuid)) + : null; } /** @@ -289,16 +280,14 @@ class KktFiscalizer extends AtolClient * @param string $uuid UUID регистрации * @param int $retry_count Количество попыток * @param int $timeout Таймаут в секундах между попытками - * @return KktResponse + * @return KktResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws GuzzleException * @throws InvalidUuidException - * @throws TooLongLoginException - * @throws TooLongPasswordException */ - public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): KktResponse + public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): ?KktResponse { $try = 0; do { @@ -317,62 +306,73 @@ class KktFiscalizer extends AtolClient * Отправляет документ на регистрацию * * @param string $api_method Метод API - * @param string $type Тип документа: receipt, correction - * @param Document $document Объект документа - * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse - * @throws AuthFailedException Ошибка авторизации - * @throws InvalidDocumentTypeException Некорректный тип документа - * @throws InvalidInnLengthException Некорректная длина ИНН - * @throws TooLongPaymentAddressException Слишком длинный адрес места расчётов + * @param Receipt|Correction $document Документ + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException - * @throws Exception + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException */ protected function registerDocument( string $api_method, - string $type, - Document $document, + Receipt|Correction $document, ?string $external_id = null - ): KktResponse { - $type = trim($type); - if (!in_array($type, ['receipt', 'correction'])) { - throw new InvalidDocumentTypeException($type); - } - $this->auth(); - if ($this->isTestMode()) { - $document->setCompany(new Company( - 'test@example.com', - TestEnvParams::FFD105()['sno'], - TestEnvParams::FFD105()['inn'], - TestEnvParams::FFD105()['payment_address'], - )); - } - $data['timestamp'] = date('d.m.y H:i:s'); - $data['external_id'] = $external_id ?: Uuid::uuid4()->toString(); - $data[$type] = $document; - if ($this->getCallbackUrl()) { - $data['service'] = ['callback_url' => $this->getCallbackUrl()]; - } - return $this->sendRequest('POST', trim($api_method), $data); + ): ?KktResponse { + $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/v1/getToken' - : 'https://online.atol.ru/possystem/v1/getToken'; + ? '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); + } } diff --git a/src/Api/KktMonitor.php b/src/Api/KktMonitor.php index a2989f0..b513f37 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/KktMonitor.php @@ -13,9 +13,11 @@ namespace AtolOnline\Api; use AtolOnline\Entities\Kkt; use AtolOnline\Exceptions\{ + AuthFailedException, + EmptyLoginException, EmptyMonitorDataException, - NotEnoughMonitorDataException -}; + EmptyPasswordException, + NotEnoughMonitorDataException}; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Collection; use JetBrains\PhpStorm\Pure; @@ -54,16 +56,21 @@ class KktMonitor extends AtolClient * * @param int|null $limit * @param int|null $offset - * @return KktResponse + * @return KktResponse|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): KktResponse + protected function fetchAll(?int $limit = null, ?int $offset = null): ?KktResponse { $params = []; !is_null($limit) && $params['limit'] = $limit; !is_null($offset) && $params['offset'] = $offset; - return $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params); + return $this->auth() + ? $this->sendRequest('GET', self::getUrlToMethod('cash-registers'), $params) + : null; } /** @@ -72,6 +79,9 @@ class KktMonitor extends AtolClient * @param int|null $limit * @param int|null $offset * @return Collection + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException * @throws GuzzleException * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 9 */ diff --git a/src/Entities/Client.php b/src/Entities/Client.php index 6a1e10b..8011274 100644 --- a/src/Entities/Client.php +++ b/src/Entities/Client.php @@ -132,10 +132,10 @@ final class Client extends Entity public function jsonSerialize(): array { $json = []; - $this->getName() && $json['name'] = $this->getName(); - $this->getEmail() && $json['email'] = $this->getEmail(); - $this->getPhone() && $json['phone'] = $this->getPhone(); - $this->getInn() && $json['inn'] = $this->getInn(); + !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; } } diff --git a/src/Entities/Correction.php b/src/Entities/Correction.php index 30a2736..2df653c 100644 --- a/src/Entities/Correction.php +++ b/src/Entities/Correction.php @@ -10,13 +10,22 @@ namespace AtolOnline\Entities; use AtolOnline\{ + Api\KktFiscalizer, + Api\KktResponse, Collections\Payments, Collections\Vats, Constants\Constraints}; use AtolOnline\Exceptions\{ + AuthFailedException, + EmptyLoginException, + EmptyPasswordException, InvalidEntityInCollectionException, - TooLongCashierException}; + InvalidInnLengthException, + InvalidPaymentAddressException, + TooLongCashierException, + TooLongPaymentAddressException}; use Exception; +use GuzzleHttp\Exception\GuzzleException; use JetBrains\PhpStorm\ArrayShape; /** @@ -26,6 +35,11 @@ use JetBrains\PhpStorm\ArrayShape; */ class Correction extends Entity { + /** + * Тип документа + */ + public const DOC_TYPE = 'correction'; + /** * @var Company Продавец */ @@ -194,6 +208,46 @@ class Correction extends Entity return $this; } + /** + * Регистрирует коррекцию прихода по текущему документу + * + * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException + */ + public function sellCorrect(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + { + return $fiscalizer->sellCorrect($this, $external_id); + } + + /** + * Регистрирует коррекцию расхода по текущему документу + * + * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException + */ + public function buyCorrect(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + { + return $fiscalizer->buyCorrect($this, $external_id); + } + /** * @inheritDoc * @throws InvalidEntityInCollectionException diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index 24c524c..99f3ec1 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -16,6 +16,7 @@ namespace AtolOnline\Entities; use ArrayAccess; use BadMethodCallException; use Illuminate\Contracts\Support\Arrayable; +use JetBrains\PhpStorm\ArrayShape; use JsonSerializable; use Stringable; @@ -32,6 +33,13 @@ abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayA /** * @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(); diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index ee5183b..ceb3e87 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -11,15 +11,24 @@ declare(strict_types = 1); namespace AtolOnline\Entities; +use AtolOnline\Api\KktFiscalizer; +use AtolOnline\Api\KktResponse; 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; /** * Класс, описывающий документ прихода, расхода, возврата прихода, возврата расхода @@ -28,6 +37,11 @@ use Exception; */ final class Receipt extends Entity { + /** + * Тип документа + */ + public const DOC_TYPE = 'receipt'; + /** * @var Client Покупатель */ @@ -360,6 +374,86 @@ final class Receipt extends Entity return $this; } + /** + * Регистрирует приход по текущему документу + * + * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException + */ + public function sell(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + { + return $fiscalizer->sell($this, $external_id); + } + + /** + * Регистрирует возврат прихода по текущему документу + * + * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException + */ + public function sellRefund(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + { + return $fiscalizer->sellRefund($this, $external_id); + } + + /** + * Регистрирует расход по текущему документу + * + * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException + */ + public function buy(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + { + return $fiscalizer->buy($this, $external_id); + } + + /** + * Регистрирует возврат расхода по текущему документу + * + * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) + * @return KktResponse|null + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws TooLongPaymentAddressException + */ + public function buyRefund(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + { + return $fiscalizer->buyRefund($this, $external_id); + } + /** * Возвращает массив для кодирования в json * @@ -368,18 +462,19 @@ final class Receipt extends Entity public function jsonSerialize(): array { $json = [ - 'client' => $this->getClient(), - 'company' => $this->getCompany(), + '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(); - $this->getSupplier()?->jsonSerialize() && $json['supplier_info'] = $this->getSupplier(); + $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(); + $this->getAddUserProps()?->jsonSerialize() && + $json['additional_user_props'] = $this->getAddUserProps()->jsonSerialize(); return $json; } } diff --git a/src/Enums/DocumentTypes.php b/src/Enums/DocumentTypes.php deleted file mode 100644 index 8cf0869..0000000 --- a/src/Enums/DocumentTypes.php +++ /dev/null @@ -1,36 +0,0 @@ -message . implode(', ', $props_diff)); diff --git a/src/Exceptions/TooHighItemPriceException.php b/src/Exceptions/TooHighItemPriceException.php index 61a813d..84c6930 100644 --- a/src/Exceptions/TooHighItemPriceException.php +++ b/src/Exceptions/TooHighItemPriceException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Ffd105Tags; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при попытке указать слишком высокую цену (сумму) предмета расчёта @@ -28,6 +29,7 @@ class TooHighItemPriceException extends TooManyException * @param string $name * @param float $price */ + #[Pure] public function __construct(string $name, float $price) { parent::__construct($price, "Слишком высокая цена для предмета расчёта '$name'"); diff --git a/src/Exceptions/TooHighItemQuantityException.php b/src/Exceptions/TooHighItemQuantityException.php index 785e0a8..245e9a6 100644 --- a/src/Exceptions/TooHighItemQuantityException.php +++ b/src/Exceptions/TooHighItemQuantityException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Ffd105Tags; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при попытке добавить слишком большое количество предмета расчёта @@ -28,6 +29,7 @@ class TooHighItemQuantityException extends TooManyException * @param string $name * @param float $quantity */ + #[Pure] public function __construct(string $name, float $quantity) { parent::__construct($quantity, "Слишком большое количество предмета расчёта '$name'"); diff --git a/src/Exceptions/TooHighItemSumException.php b/src/Exceptions/TooHighItemSumException.php index b296bee..66d7b13 100644 --- a/src/Exceptions/TooHighItemSumException.php +++ b/src/Exceptions/TooHighItemSumException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Ffd105Tags; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при попытке получеиня слишком высокой стоимости предмета расчёта @@ -28,6 +29,7 @@ class TooHighItemSumException extends TooManyException * @param string $name * @param float $sum */ + #[Pure] public function __construct(string $name, float $sum) { parent::__construct($sum, "Слишком высокая стоимость предмета расчёта '$name'"); diff --git a/src/Exceptions/TooLongCallbackUrlException.php b/src/Exceptions/TooLongCallbackUrlException.php index b5e9307..0d39009 100644 --- a/src/Exceptions/TooLongCallbackUrlException.php +++ b/src/Exceptions/TooLongCallbackUrlException.php @@ -18,6 +18,6 @@ use AtolOnline\Constants\Constraints; */ class TooLongCallbackUrlException extends TooLongException { - protected $message = 'Слишком длинный адрес колбека'; + protected $message = 'Слишком длинный callback_url'; protected float $max = Constraints::MAX_LENGTH_CALLBACK_URL; } diff --git a/src/Exceptions/TooLongException.php b/src/Exceptions/TooLongException.php index 952af10..dcb5632 100644 --- a/src/Exceptions/TooLongException.php +++ b/src/Exceptions/TooLongException.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use JetBrains\PhpStorm\Pure; + /** * Исключение, возникающее при попытке указать слишком длинное что-либо */ @@ -33,6 +35,7 @@ class TooLongException extends AtolException * @param string $message * @param float $max */ + #[Pure] public function __construct(string $value, string $message = '', float $max = 0) { parent::__construct( diff --git a/src/Exceptions/TooLongItemCodeException.php b/src/Exceptions/TooLongItemCodeException.php index e9dba47..79ff94a 100644 --- a/src/Exceptions/TooLongItemCodeException.php +++ b/src/Exceptions/TooLongItemCodeException.php @@ -13,6 +13,7 @@ namespace AtolOnline\Exceptions; use AtolOnline\Constants\Constraints; use AtolOnline\Constants\Ffd105Tags; +use JetBrains\PhpStorm\Pure; /** * Исключение, возникающее при попытке указать слишком длинный код товара @@ -28,6 +29,7 @@ class TooLongItemCodeException extends TooLongException * @param string $name * @param string $code */ + #[Pure] public function __construct(string $name, string $code) { parent::__construct($code, "Слишком длинный код товара '$name'"); diff --git a/src/Exceptions/TooManyException.php b/src/Exceptions/TooManyException.php index d999287..0d715d7 100644 --- a/src/Exceptions/TooManyException.php +++ b/src/Exceptions/TooManyException.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; +use JetBrains\PhpStorm\Pure; + /** * Исключение, возникающее при попытке указать слишком большое количество чего-либо */ @@ -33,6 +35,7 @@ class TooManyException extends AtolException * @param string $message * @param float|null $max */ + #[Pure] public function __construct(float $value, string $message = '', ?float $max = null) { parent::__construct( diff --git a/tests/AtolOnline/Tests/Api/KktFiscalizerTest.php b/tests/AtolOnline/Tests/Api/KktFiscalizerTest.php new file mode 100644 index 0000000..cee78db --- /dev/null +++ b/tests/AtolOnline/Tests/Api/KktFiscalizerTest.php @@ -0,0 +1,417 @@ +assertIsObject($fisc); + $this->assertIsSameClass(KktFiscalizer::class, $fisc); + $this->assertExtendsClasses([AtolClient::class], $fisc); + } + + /** + * Тестирует установку и возврат группы ККТ + * + * @return void + * @covers \AtolOnline\Api\KktFiscalizer + * @covers \AtolOnline\Api\KktFiscalizer::getGroup + * @covers \AtolOnline\Api\KktFiscalizer::setGroup + */ + public function testGroup(): void + { + // test mode + $this->assertEquals( + TestEnvParams::FFD105()['group'], + (new KktFiscalizer(group: 'group'))->getGroup() + ); + // prod mode + $this->assertEquals('group', (new KktFiscalizer(false, group: 'group'))->getGroup()); + $this->assertNull((new KktFiscalizer(false))->getGroup()); + } + + /** + * Тестирует выброс исключения при попытке передать пустую группу ККТ в конструктор + * + * @return void + * @covers \AtolOnline\Api\KktFiscalizer + * @covers \AtolOnline\Api\KktFiscalizer::setGroup + * @covers \AtolOnline\Exceptions\EmptyGroupException + */ + public function testEmptyGroupException(): void + { + $this->expectException(EmptyGroupException::class); + new KktFiscalizer(group: "\n\r \0\t"); + } + + /** + * Тестирует выброс исключения при попытке установить слишком длинный адрес колбека + * + * @return void + * @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl + * @covers \AtolOnline\Exceptions\TooLongCallbackUrlException + * @throws InvalidCallbackUrlException + * @throws TooLongCallbackUrlException + */ + public function testTooLongCallbackUrlException(): void + { + $this->expectException(TooLongCallbackUrlException::class); + (new KktFiscalizer())->setCallbackUrl(Helpers::randomStr(Constraints::MAX_LENGTH_CALLBACK_URL + 1)); + } + + /** + * Тестирует выброс исключения при попытке установить слишком длинный адрес колбека + * + * @return void + * @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl + * @covers \AtolOnline\Exceptions\InvalidCallbackUrlException + * @throws InvalidCallbackUrlException + * @throws TooLongCallbackUrlException + */ + public function testInvalidCallbackUrlException(): void + { + $this->expectException(InvalidCallbackUrlException::class); + (new KktFiscalizer())->setCallbackUrl(Helpers::randomStr()); + } + + /** + * Тестирует обнуление адреса колбека + * + * @param mixed $param + * @return void + * @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl + * @covers \AtolOnline\Api\KktFiscalizer::getCallbackUrl + * @dataProvider providerNullableStrings + * @throws InvalidCallbackUrlException + * @throws TooLongCallbackUrlException + */ + public function testNullableCallbackUrl(mixed $param): void + { + $this->assertNull((new KktFiscalizer())->setCallbackUrl($param)->getCallbackUrl()); + } + + /** + * Тестирует регистрацию документа прихода + * + * @return void + * @covers \AtolOnline\Entities\Receipt::sell + * @covers \AtolOnline\Api\KktFiscalizer::sell + * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl + * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @throws AuthFailedException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws TooLongPaymentAddressException + * @throws TooManyException + * @throws GuzzleException + */ + public function testSell(): void + { + $fisc_result = $this->newReceipt()->sell(new KktFiscalizer()); + $this->assertTrue($fisc_result->isValid()); + $this->assertEquals('wait', $fisc_result->getContent()->status); + self::$registered_uuids[] = $fisc_result->getContent()->uuid; + } + + /** + * Тестирует регистрацию документа возврата прихода + * + * @return void + * @covers \AtolOnline\Entities\Receipt::sellRefund + * @covers \AtolOnline\Api\KktFiscalizer::sellRefund + * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl + * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @throws AuthFailedException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws TooLongPaymentAddressException + * @throws TooManyException + * @throws GuzzleException + */ + public function testSellRefund(): void + { + $fisc_result = $this->newReceipt()->sellRefund(new KktFiscalizer()); + $this->assertTrue($fisc_result->isValid()); + $this->assertEquals('wait', $fisc_result->getContent()->status); + self::$registered_uuids[] = $fisc_result->getContent()->uuid; + } + + /** + * Тестирует регистрацию документа возврата прихода + * + * @return void + * @covers \AtolOnline\Entities\Correction::sellCorrect + * @covers \AtolOnline\Api\KktFiscalizer::sellCorrect + * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl + * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws TooLongPaymentAddressException + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + */ + public function testSellCorrect(): void + { + $fisc_result = $this->newCorrection()->sellCorrect(new KktFiscalizer()); + $this->assertTrue($fisc_result->isValid()); + $this->assertEquals('wait', $fisc_result->getContent()->status); + self::$registered_uuids[] = $fisc_result->getContent()->uuid; + } + + /** + * Тестирует регистрацию документа расхода + * + * @return void + * @covers \AtolOnline\Entities\Receipt::buy + * @covers \AtolOnline\Api\KktFiscalizer::buy + * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl + * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @throws AuthFailedException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws TooLongPaymentAddressException + * @throws TooManyException + * @throws GuzzleException + */ + public function testBuy(): void + { + $fisc_result = $this->newReceipt()->buy(new KktFiscalizer()); + $this->assertTrue($fisc_result->isValid()); + $this->assertEquals('wait', $fisc_result->getContent()->status); + self::$registered_uuids[] = $fisc_result->getContent()->uuid; + } + + /** + * Тестирует регистрацию документа возврата расхода + * + * @return void + * @covers \AtolOnline\Entities\Receipt::buyRefund + * @covers \AtolOnline\Api\KktFiscalizer::buyRefund + * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl + * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @throws AuthFailedException + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws TooLongPaymentAddressException + * @throws TooManyException + * @throws GuzzleException + */ + public function testBuyRefund(): void + { + $fisc_result = $this->newReceipt()->buyRefund(new KktFiscalizer()); + $this->assertTrue($fisc_result->isValid()); + $this->assertEquals('wait', $fisc_result->getContent()->status); + self::$registered_uuids[] = $fisc_result->getContent()->uuid; + } + + /** + * Тестирует регистрацию документа возврата прихода + * + * @return void + * @covers \AtolOnline\Entities\Correction::buyCorrect + * @covers \AtolOnline\Api\KktFiscalizer::buyCorrect + * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl + * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws InvalidInnLengthException + * @throws InvalidPaymentAddressException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws TooLongLoginException + * @throws TooLongPasswordException + * @throws TooLongPaymentAddressException + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + */ + public function testBuyCorrect(): void + { + $fisc_result = $this->newCorrection()->buyCorrect(new KktFiscalizer()); + $this->assertTrue($fisc_result->isValid()); + $this->assertEquals('wait', $fisc_result->getContent()->status); + self::$registered_uuids[] = $fisc_result->getContent()->uuid; + } + + /** + * Тестирует разовое получение статуса фискализации документа + * + * @return void + * @covers \AtolOnline\Api\KktFiscalizer::getDocumentStatus + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidUuidException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function testGetDocumentStatus(): void + { + $fisc_status = (new KktFiscalizer())->getDocumentStatus(self::$registered_uuids[0]); + $this->assertTrue($fisc_status->isValid()); + $this->assertTrue(in_array($fisc_status->getContent()->status, ['wait', 'done'])); + } + + /** + * Тестирует опрос API на получение статуса фискализации документа + * + * @return void + * @covers \AtolOnline\Api\KktFiscalizer::pollDocumentStatus + * @throws AuthFailedException + * @throws EmptyLoginException + * @throws EmptyPasswordException + * @throws GuzzleException + * @throws InvalidUuidException + * @throws TooLongLoginException + * @throws TooLongPasswordException + */ + public function testPollDocumentStatus(): void + { + $fisc_status = (new KktFiscalizer())->pollDocumentStatus(self::$registered_uuids[1]); + $this->assertTrue($fisc_status->isValid()); + $this->assertEquals('done', $fisc_status->getContent()->status); + } + +} diff --git a/tests/AtolOnline/Tests/Api/KktMonitorTest.php b/tests/AtolOnline/Tests/Api/KktMonitorTest.php index af0836b..56b7753 100644 --- a/tests/AtolOnline/Tests/Api/KktMonitorTest.php +++ b/tests/AtolOnline/Tests/Api/KktMonitorTest.php @@ -92,14 +92,14 @@ class KktMonitorTest extends BasicTestCase */ public function testLogin(): void { - $client = new KktMonitor(login: 'login'); + $client = new KktMonitor(false, login: 'login'); $this->assertEquals('login', $client->getLogin()); $client = new KktMonitor(); - $this->assertNull($client->getLogin()); + $this->assertEquals(TestEnvParams::FFD105()['login'], $client->getLogin()); $client->setLogin('login'); - $this->assertEquals('login', $client->getLogin()); + $this->assertEquals(TestEnvParams::FFD105()['login'], $client->getLogin()); } /** @@ -143,14 +143,14 @@ class KktMonitorTest extends BasicTestCase */ public function testPassword(): void { - $client = new KktMonitor(password: 'password'); + $client = new KktMonitor(false, password: 'password'); $this->assertEquals('password', $client->getPassword()); $client = new KktMonitor(); - $this->assertNull($client->getPassword()); + $this->assertEquals(TestEnvParams::FFD105()['password'], $client->getPassword()); $client->setPassword('password'); - $this->assertEquals('password', $client->getPassword()); + $this->assertEquals(TestEnvParams::FFD105()['password'], $client->getPassword()); } /** @@ -262,7 +262,7 @@ class KktMonitorTest extends BasicTestCase * Тестирует возврат объекта последнего ответа от API * * @depends testAuth - * @covers \AtolOnline\Api\KktMonitor::getResponse + * @covers \AtolOnline\Api\KktMonitor::getLastResponse * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException @@ -276,7 +276,7 @@ class KktMonitorTest extends BasicTestCase $this->skipIfMonitoringIsOffline(); $client = $this->newTestClient(); $client->auth(); - $this->assertIsSameClass(KktResponse::class, $client->getResponse()); + $this->assertIsSameClass(KktResponse::class, $client->getLastResponse()); } /** @@ -301,7 +301,8 @@ class KktMonitorTest extends BasicTestCase $client = $this->newTestClient(); $client->auth(); $kkts = $client->getAll(); - $this->assertNotEmpty($client->getResponse()->getContent()); + $sss = $kkts->where('deviceNumber', 'KKT014034'); + $this->assertNotEmpty($client->getLastResponse()->getContent()); $this->assertIsCollection($kkts); $this->assertTrue($kkts->count() > 0); $this->assertIsSameClass(Kkt::class, $kkts->random()); @@ -336,7 +337,7 @@ class KktMonitorTest extends BasicTestCase $client->auth(); $serial_number = $client->getAll()->first()->serialNumber; $kkt = $client->getOne($serial_number); - $this->assertNotEmpty($client->getResponse()); + $this->assertNotEmpty($client->getLastResponse()); $this->assertIsSameClass(Kkt::class, $kkt); $this->assertIsAtolable($kkt); $this->assertNotNull($kkt->serialNumber); diff --git a/tests/AtolOnline/Tests/BasicTestCase.php b/tests/AtolOnline/Tests/BasicTestCase.php index 22f1a1b..8f576d6 100644 --- a/tests/AtolOnline/Tests/BasicTestCase.php +++ b/tests/AtolOnline/Tests/BasicTestCase.php @@ -12,13 +12,27 @@ declare(strict_types = 1); namespace AtolOnline\Tests; use AtolOnline\Collections\EntityCollection; +use AtolOnline\Collections\Items; +use AtolOnline\Collections\Payments; +use AtolOnline\Collections\Vats; +use AtolOnline\Entities\Client; +use AtolOnline\Entities\Company; +use AtolOnline\Entities\Correction; +use AtolOnline\Entities\CorrectionInfo; use AtolOnline\Entities\Entity; use AtolOnline\Entities\Item; use AtolOnline\Entities\Payment; +use AtolOnline\Entities\Receipt; use AtolOnline\Entities\Vat; +use AtolOnline\Enums\CorrectionTypes; use AtolOnline\Enums\PaymentTypes; +use AtolOnline\Enums\SnoTypes; use AtolOnline\Enums\VatTypes; +use AtolOnline\Exceptions\EmptyCorrectionNumberException; use AtolOnline\Exceptions\EmptyItemNameException; +use AtolOnline\Exceptions\EmptyItemsException; +use AtolOnline\Exceptions\InvalidCorrectionDateException; +use AtolOnline\Exceptions\InvalidEntityInCollectionException; use AtolOnline\Exceptions\InvalidEnumValueException; use AtolOnline\Exceptions\NegativeItemPriceException; use AtolOnline\Exceptions\NegativeItemQuantityException; @@ -29,7 +43,7 @@ use AtolOnline\Exceptions\TooLongItemNameException; use AtolOnline\Exceptions\TooManyException; use AtolOnline\Helpers; use Exception; -use GuzzleHttp\Client; +use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Collection; use PHPUnit\Framework\TestCase; @@ -53,7 +67,7 @@ class BasicTestCase extends TestCase protected function ping(string $url, int $code): bool { try { - $result = (new Client([ + $result = (new GuzzleClient([ 'http_errors' => false, 'timeout' => 3, ]))->request('GET', $url); @@ -387,4 +401,51 @@ class BasicTestCase extends TestCase } return $result; } + + /** + * Возвращает валидный тестовый объект чека прихода + * + * @return Receipt + * @throws EmptyItemNameException + * @throws EmptyItemsException + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativeItemPriceException + * @throws NegativeItemQuantityException + * @throws NegativePaymentSumException + * @throws TooHighItemPriceException + * @throws TooHighPaymentSumException + * @throws TooLongItemNameException + * @throws TooManyException + */ + protected function newReceipt(): Receipt + { + return new Receipt( + new Client('John Doe', 'john@example.com', '+79501234567', '1234567890'), + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new Items($this->generateItemObjects(2)), + new Payments($this->generatePaymentObjects()) + ); + } + + /** + * Возвращает валидный тестовый объект чека + * + * @return Correction + * @throws InvalidEntityInCollectionException + * @throws InvalidEnumValueException + * @throws NegativePaymentSumException + * @throws TooHighPaymentSumException + * @throws EmptyCorrectionNumberException + * @throws InvalidCorrectionDateException + */ + protected function newCorrection(): Correction + { + return new Correction( + new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), + new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', Helpers::randomStr()), + new Payments($this->generatePaymentObjects(2)), + new Vats($this->generateVatObjects(2)), + ); + } } diff --git a/tests/AtolOnline/Tests/Entities/CorrectionTest.php b/tests/AtolOnline/Tests/Entities/CorrectionTest.php index 759cde4..7cef2b2 100644 --- a/tests/AtolOnline/Tests/Entities/CorrectionTest.php +++ b/tests/AtolOnline/Tests/Entities/CorrectionTest.php @@ -9,18 +9,18 @@ namespace AtolOnline\Tests\Entities; -use AtolOnline\{Constants\Constraints, Helpers, Tests\BasicTestCase}; -use AtolOnline\Collections\{Payments, Vats,}; -use AtolOnline\Entities\{Company, Correction, CorrectionInfo}; -use AtolOnline\Enums\{CorrectionTypes, SnoTypes}; -use AtolOnline\Exceptions\{EmptyCorrectionNumberException, +use AtolOnline\{ + Constants\Constraints, + Helpers, + Tests\BasicTestCase}; +use AtolOnline\Exceptions\{ + EmptyCorrectionNumberException, InvalidCorrectionDateException, InvalidEntityInCollectionException, InvalidEnumValueException, NegativePaymentSumException, TooHighPaymentSumException, - TooLongCashierException -}; + TooLongCashierException}; use Exception; /** @@ -114,25 +114,4 @@ class CorrectionTest extends BasicTestCase $this->expectException(TooLongCashierException::class); $this->newCorrection()->setCashier(Helpers::randomStr(Constraints::MAX_LENGTH_CASHIER_NAME + 1)); } - - /** - * Возвращает валидный тестовый объект чека - * - * @return Correction - * @throws InvalidEntityInCollectionException - * @throws InvalidEnumValueException - * @throws NegativePaymentSumException - * @throws TooHighPaymentSumException - * @throws EmptyCorrectionNumberException - * @throws InvalidCorrectionDateException - */ - protected function newCorrection(): Correction - { - return new Correction( - new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), - new CorrectionInfo(CorrectionTypes::SELF, '01.01.2021', Helpers::randomStr()), - new Payments($this->generatePaymentObjects(2)), - new Vats($this->generateVatObjects(2)), - ); - } } diff --git a/tests/AtolOnline/Tests/Entities/ReceiptTest.php b/tests/AtolOnline/Tests/Entities/ReceiptTest.php index 5665af5..ce3151b 100644 --- a/tests/AtolOnline/Tests/Entities/ReceiptTest.php +++ b/tests/AtolOnline/Tests/Entities/ReceiptTest.php @@ -127,7 +127,7 @@ class ReceiptTest extends BasicTestCase ); $receipt = $this->newReceipt()->setAgentInfo($agent_info); $this->assertArrayHasKey('agent_info', $receipt->jsonSerialize()); - $this->assertEquals($receipt->getAgentInfo(), $receipt->jsonSerialize()['agent_info']); + $this->assertEquals($receipt->getAgentInfo()->jsonSerialize(), $receipt->jsonSerialize()['agent_info']); $this->assertArrayNotHasKey('agent_info', $receipt->setAgentInfo(null)->jsonSerialize()); } @@ -159,7 +159,7 @@ class ReceiptTest extends BasicTestCase $supplier = new Supplier('some name', '+fasd3\qe3fs_=nac99013928czc', ['+122997365456']); $receipt = $this->newReceipt()->setSupplier($supplier); $this->assertArrayHasKey('supplier_info', $receipt->jsonSerialize()); - $this->assertEquals($receipt->getSupplier(), $receipt->jsonSerialize()['supplier_info']); + $this->assertEquals($receipt->getSupplier()->jsonSerialize(), $receipt->jsonSerialize()['supplier_info']); $this->assertArrayNotHasKey('supplier_info', $receipt->setSupplier(null)->jsonSerialize()); } @@ -207,7 +207,9 @@ class ReceiptTest extends BasicTestCase public function testInvalidItemInCollectionException(): void { $this->expectException(InvalidEntityInCollectionException::class); - $this->expectErrorMessage('Коллекция AtolOnline\Collections\Items должна содержать объекты AtolOnline\Entities\Item'); + $this->expectErrorMessage( + 'Коллекция AtolOnline\Collections\Items должна содержать объекты AtolOnline\Entities\Item' + ); new Receipt( new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), @@ -267,7 +269,9 @@ class ReceiptTest extends BasicTestCase public function testInvalidPaymentInCollectionException(): void { $this->expectException(InvalidEntityInCollectionException::class); - $this->expectErrorMessage('Коллекция AtolOnline\Collections\Payments должна содержать объекты AtolOnline\Entities\Payment'); + $this->expectErrorMessage( + 'Коллекция AtolOnline\Collections\Payments должна содержать объекты AtolOnline\Entities\Payment' + ); (string)new Receipt( new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), @@ -330,7 +334,9 @@ class ReceiptTest extends BasicTestCase public function testInvalidVatInCollectionException(): void { $this->expectException(InvalidEntityInCollectionException::class); - $this->expectErrorMessage('Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat'); + $this->expectErrorMessage( + 'Коллекция AtolOnline\Collections\Vats должна содержать объекты AtolOnline\Entities\Vat' + ); (string)$this->newReceipt()->setVats(new Vats(['qwerty'])); } @@ -357,15 +363,8 @@ class ReceiptTest extends BasicTestCase */ public function testCalculations(): void { - $items_total = 0; $receipt = $this->newReceipt(); - - //TODO при $receipt->getItems()->pluck('sum') стреляет InvalidEntityInCollectionException - // см. примечания в конструкторе EntityCollection - $receipt->getItems()->each(function ($item) use (&$items_total) { - /** @var Item $item */ - return $items_total += $item->getSum(); - }); + $items_total = $receipt->getItems()->pluck('sum')->sum(); $this->assertEquals($items_total, $receipt->getTotal()); /** @var Vat $vat */ @@ -563,34 +562,10 @@ class ReceiptTest extends BasicTestCase $aup = new AdditionalUserProps('name', 'value'); $receipt = $this->newReceipt()->setAddUserProps($aup); $this->assertArrayHasKey('additional_user_props', $receipt->jsonSerialize()); - $this->assertEquals($receipt->getAddUserProps(), $receipt->jsonSerialize()['additional_user_props']); + $this->assertEquals( + $receipt->getAddUserProps()->jsonSerialize(), + $receipt->jsonSerialize()['additional_user_props'] + ); $this->assertArrayNotHasKey('additional_user_props', $receipt->setAddUserProps(null)->jsonSerialize()); } - - /** - * Возвращает валидный тестовый объект чека - * - * @return Receipt - * @throws EmptyItemNameException - * @throws EmptyItemsException - * @throws EmptyPaymentsException - * @throws InvalidEntityInCollectionException - * @throws InvalidEnumValueException - * @throws NegativeItemPriceException - * @throws NegativeItemQuantityException - * @throws NegativePaymentSumException - * @throws TooHighItemPriceException - * @throws TooHighPaymentSumException - * @throws TooLongItemNameException - * @throws TooManyException - */ - protected function newReceipt(): Receipt - { - return new Receipt( - new Client('John Doe', 'john@example.com', '+1/22/99*73s dsdas654 5s6', '+fasd3\qe3fs_=nac99013928czc'), - new Company('company@example.com', SnoTypes::OSN, '1234567890', 'https://example.com'), - new Items($this->generateItemObjects(2)), - new Payments($this->generatePaymentObjects()) - ); - } } From fdc5ab112a485952c2156af72aa8c2208cc8f7ee Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 19 Dec 2021 22:29:17 +0800 Subject: [PATCH 81/85] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B8=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=81=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=83=D1=89=D0=B5=D0=B9=20=D0=BF=D1=80=D0=BE=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/AtolClient.php | 17 +- src/Api/{KktResponse.php => AtolResponse.php} | 4 +- src/Api/{KktFiscalizer.php => Fiscalizer.php} | 40 ++--- src/Api/{KktMonitor.php => Monitor.php} | 10 +- src/Entities/Correction.php | 16 +- src/Entities/Entity.php | 2 +- src/Entities/Receipt.php | 28 +-- src/Exceptions/AuthFailedException.php | 6 +- ...tFiscalizerTest.php => FiscalizerTest.php} | 167 ++++++++---------- .../{KktMonitorTest.php => MonitorTest.php} | 125 ++++++------- 10 files changed, 199 insertions(+), 216 deletions(-) rename src/Api/{KktResponse.php => AtolResponse.php} (96%) rename src/Api/{KktFiscalizer.php => Fiscalizer.php} (94%) rename src/Api/{KktMonitor.php => Monitor.php} (95%) rename tests/AtolOnline/Tests/Api/{KktFiscalizerTest.php => FiscalizerTest.php} (68%) rename tests/AtolOnline/Tests/Api/{KktMonitorTest.php => MonitorTest.php} (74%) diff --git a/src/Api/AtolClient.php b/src/Api/AtolClient.php index 2c7b608..08caa92 100644 --- a/src/Api/AtolClient.php +++ b/src/Api/AtolClient.php @@ -35,9 +35,9 @@ abstract class AtolClient protected array $request; /** - * @var KktResponse|null Последний ответ сервера АТОЛ + * @var AtolResponse|null Последний ответ сервера АТОЛ */ - protected ?KktResponse $response; + protected ?AtolResponse $response; /** * @var bool Флаг тестового режима @@ -106,9 +106,9 @@ abstract class AtolClient /** * Возвращает последний ответ сервера * - * @return KktResponse|null + * @return AtolResponse|null */ - public function getLastResponse(): ?KktResponse + public function getLastResponse(): ?AtolResponse { return $this->response; } @@ -263,7 +263,7 @@ abstract class AtolClient 'login' => $this->getLogin() ?? throw new EmptyLoginException(), 'pass' => $this->getPassword() ?? throw new EmptyPasswordException(), ]); - if (!$result->isValid() || !$result->getContent()->token) { + if (!$result->isSuccessful() || !$result->getContent()->token) { throw new AuthFailedException($result); } return $result->getContent()?->token; @@ -276,7 +276,7 @@ abstract class AtolClient * @param string $url URL * @param array|null $data Данные для передачи * @param array|null $options Параметры Guzzle - * @return KktResponse + * @return AtolResponse * @throws GuzzleException * @see https://guzzle.readthedocs.io/en/latest/request-options.html */ @@ -285,7 +285,7 @@ abstract class AtolClient string $url, ?array $data = null, ?array $options = null - ): KktResponse { + ): AtolResponse { $http_method = strtoupper(trim($http_method)); $options['headers'] = array_merge($this->getHeaders(), $options['headers'] ?? []); $http_method != 'GET' && $options['json'] = $data; @@ -294,7 +294,7 @@ abstract class AtolClient 'url' => $url, ], $options); $response = $this->http->request($http_method, $url, $options); - return $this->response = new KktResponse($response); + return $this->response = new AtolResponse($response); } /** @@ -328,5 +328,4 @@ abstract class AtolClient * @return string */ abstract protected function getMainEndpoint(): string; - } diff --git a/src/Api/KktResponse.php b/src/Api/AtolResponse.php similarity index 96% rename from src/Api/KktResponse.php rename to src/Api/AtolResponse.php index 78988af..41fb3f1 100644 --- a/src/Api/KktResponse.php +++ b/src/Api/AtolResponse.php @@ -27,7 +27,7 @@ use Stringable; * @property mixed $error * @package AtolOnline\Api */ -class KktResponse implements JsonSerializable, Stringable +class AtolResponse implements JsonSerializable, Stringable { /** * @var int Код ответа сервера @@ -104,7 +104,7 @@ class KktResponse implements JsonSerializable, Stringable * @return bool */ #[Pure] - public function isValid(): bool + public function isSuccessful(): bool { return !empty($this->getCode()) && !empty($this->getContent()) diff --git a/src/Api/KktFiscalizer.php b/src/Api/Fiscalizer.php similarity index 94% rename from src/Api/KktFiscalizer.php rename to src/Api/Fiscalizer.php index 6b593fb..8bcbe78 100644 --- a/src/Api/KktFiscalizer.php +++ b/src/Api/Fiscalizer.php @@ -38,7 +38,7 @@ use Ramsey\Uuid\Uuid; /** * Класс фискализатора для регистрации документов на ККТ */ -class KktFiscalizer extends AtolClient +class Fiscalizer extends AtolClient { /** * @var string|null Группа ККТ @@ -139,7 +139,7 @@ class KktFiscalizer extends AtolClient * * @param Receipt $receipt Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -149,7 +149,7 @@ class KktFiscalizer extends AtolClient * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function sell(Receipt $receipt, ?string $external_id = null): ?KktResponse + public function sell(Receipt $receipt, ?string $external_id = null): ?AtolResponse { return $this->registerDocument('sell', $receipt, $external_id); } @@ -159,7 +159,7 @@ class KktFiscalizer extends AtolClient * * @param Receipt $receipt Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -169,7 +169,7 @@ class KktFiscalizer extends AtolClient * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function sellRefund(Receipt $receipt, ?string $external_id = null): ?KktResponse + public function sellRefund(Receipt $receipt, ?string $external_id = null): ?AtolResponse { return $this->registerDocument('sell_refund', $receipt, $external_id); } @@ -179,7 +179,7 @@ class KktFiscalizer extends AtolClient * * @param Correction $correction Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -189,7 +189,7 @@ class KktFiscalizer extends AtolClient * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function sellCorrect(Correction $correction, ?string $external_id = null): ?KktResponse + public function sellCorrect(Correction $correction, ?string $external_id = null): ?AtolResponse { return $this->registerDocument('sell_correction', $correction, $external_id); } @@ -199,7 +199,7 @@ class KktFiscalizer extends AtolClient * * @param Receipt $receipt Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -209,7 +209,7 @@ class KktFiscalizer extends AtolClient * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function buy(Receipt $receipt, ?string $external_id = null): ?KktResponse + public function buy(Receipt $receipt, ?string $external_id = null): ?AtolResponse { return $this->registerDocument('buy', $receipt, $external_id); } @@ -219,7 +219,7 @@ class KktFiscalizer extends AtolClient * * @param Receipt $receipt Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -229,7 +229,7 @@ class KktFiscalizer extends AtolClient * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function buyRefund(Receipt $receipt, ?string $external_id = null): ?KktResponse + public function buyRefund(Receipt $receipt, ?string $external_id = null): ?AtolResponse { return $this->registerDocument('buy_refund', $receipt, $external_id); } @@ -239,7 +239,7 @@ class KktFiscalizer extends AtolClient * * @param Correction $correction Объект документа * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -249,7 +249,7 @@ class KktFiscalizer extends AtolClient * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function buyCorrect(Correction $correction, ?string $external_id = null): ?KktResponse + public function buyCorrect(Correction $correction, ?string $external_id = null): ?AtolResponse { return $this->registerDocument('buy_correction', $correction, $external_id); } @@ -258,14 +258,14 @@ class KktFiscalizer extends AtolClient * Проверяет статус чека на ККТ один раз * * @param string $uuid UUID регистрации - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws GuzzleException * @throws InvalidUuidException */ - public function getDocumentStatus(string $uuid): ?KktResponse + public function getDocumentStatus(string $uuid): ?AtolResponse { !Uuid::isValid($uuid = trim($uuid)) && throw new InvalidUuidException($uuid); return $this->auth() @@ -280,19 +280,19 @@ class KktFiscalizer extends AtolClient * @param string $uuid UUID регистрации * @param int $retry_count Количество попыток * @param int $timeout Таймаут в секундах между попытками - * @return KktResponse|null + * @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): ?KktResponse + public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1): ?AtolResponse { $try = 0; do { $response = $this->getDocumentStatus($uuid); - if ($response->isValid() && $response->getContent()->status == 'done') { + if ($response->isSuccessful() && $response->getContent()->status == 'done') { break; } else { sleep($timeout); @@ -308,7 +308,7 @@ class KktFiscalizer extends AtolClient * @param string $api_method Метод API * @param Receipt|Correction $document Документ * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -322,7 +322,7 @@ class KktFiscalizer extends AtolClient string $api_method, Receipt|Correction $document, ?string $external_id = null - ): ?KktResponse { + ): ?AtolResponse { $this->isTestMode() && $document->getCompany() ->setInn(TestEnvParams::FFD105()['inn']) ->setPaymentAddress(TestEnvParams::FFD105()['payment_address']); diff --git a/src/Api/KktMonitor.php b/src/Api/Monitor.php similarity index 95% rename from src/Api/KktMonitor.php rename to src/Api/Monitor.php index b513f37..4f2eea0 100644 --- a/src/Api/KktMonitor.php +++ b/src/Api/Monitor.php @@ -27,7 +27,7 @@ use JetBrains\PhpStorm\Pure; * * @see https://online.atol.ru/files/API_service_information.pdf Документация */ -class KktMonitor extends AtolClient +class Monitor extends AtolClient { /** * @inheritDoc @@ -56,14 +56,14 @@ class KktMonitor extends AtolClient * * @param int|null $limit * @param int|null $offset - * @return KktResponse|null + * @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): ?KktResponse + protected function fetchAll(?int $limit = null, ?int $offset = null): ?AtolResponse { $params = []; !is_null($limit) && $params['limit'] = $limit; @@ -95,11 +95,11 @@ class KktMonitor extends AtolClient * Получает от API информацию о конкретной ККТ по её серийному номеру * * @param string $serial_number - * @return KktResponse + * @return AtolResponse * @throws GuzzleException * @see https://online.atol.ru/files/API_service_information.pdf Документация, стр 11 */ - protected function fetchOne(string $serial_number): KktResponse + protected function fetchOne(string $serial_number): AtolResponse { return $this->sendRequest( 'GET', diff --git a/src/Entities/Correction.php b/src/Entities/Correction.php index 2df653c..b9e4a3d 100644 --- a/src/Entities/Correction.php +++ b/src/Entities/Correction.php @@ -10,8 +10,8 @@ namespace AtolOnline\Entities; use AtolOnline\{ - Api\KktFiscalizer, - Api\KktResponse, + Api\AtolResponse, + Api\Fiscalizer, Collections\Payments, Collections\Vats, Constants\Constraints}; @@ -211,9 +211,9 @@ class Correction extends Entity /** * Регистрирует коррекцию прихода по текущему документу * - * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param Fiscalizer $fiscalizer Объект фискализатора * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -223,7 +223,7 @@ class Correction extends Entity * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function sellCorrect(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + public function sellCorrect(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse { return $fiscalizer->sellCorrect($this, $external_id); } @@ -231,9 +231,9 @@ class Correction extends Entity /** * Регистрирует коррекцию расхода по текущему документу * - * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param Fiscalizer $fiscalizer Объект фискализатора * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -243,7 +243,7 @@ class Correction extends Entity * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function buyCorrect(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + public function buyCorrect(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse { return $fiscalizer->buyCorrect($this, $external_id); } diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index 99f3ec1..86a13c7 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -38,7 +38,7 @@ abstract class Entity implements JsonSerializable, Stringable, Arrayable, ArrayA 'correction_info' => "\AtolOnline\Entities\CorrectionInfo", 'payments' => "array", 'vats' => "\AtolOnline\Collections\Vats|null", - 'cashier' => "\null|string" + 'cashier' => "null|string", ])] public function toArray() { diff --git a/src/Entities/Receipt.php b/src/Entities/Receipt.php index ceb3e87..67c7e59 100644 --- a/src/Entities/Receipt.php +++ b/src/Entities/Receipt.php @@ -11,8 +11,8 @@ declare(strict_types = 1); namespace AtolOnline\Entities; -use AtolOnline\Api\KktFiscalizer; -use AtolOnline\Api\KktResponse; +use AtolOnline\Api\AtolResponse; +use AtolOnline\Api\Fiscalizer; use AtolOnline\Collections\Items; use AtolOnline\Collections\Payments; use AtolOnline\Collections\Vats; @@ -377,9 +377,9 @@ final class Receipt extends Entity /** * Регистрирует приход по текущему документу * - * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param Fiscalizer $fiscalizer Объект фискализатора * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -389,7 +389,7 @@ final class Receipt extends Entity * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function sell(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + public function sell(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse { return $fiscalizer->sell($this, $external_id); } @@ -397,9 +397,9 @@ final class Receipt extends Entity /** * Регистрирует возврат прихода по текущему документу * - * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param Fiscalizer $fiscalizer Объект фискализатора * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -409,7 +409,7 @@ final class Receipt extends Entity * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function sellRefund(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + public function sellRefund(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse { return $fiscalizer->sellRefund($this, $external_id); } @@ -417,9 +417,9 @@ final class Receipt extends Entity /** * Регистрирует расход по текущему документу * - * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param Fiscalizer $fiscalizer Объект фискализатора * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -429,7 +429,7 @@ final class Receipt extends Entity * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function buy(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + public function buy(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse { return $fiscalizer->buy($this, $external_id); } @@ -437,9 +437,9 @@ final class Receipt extends Entity /** * Регистрирует возврат расхода по текущему документу * - * @param KktFiscalizer $fiscalizer Объект фискализатора + * @param Fiscalizer $fiscalizer Объект фискализатора * @param string|null $external_id Уникальный код документа (если не указан, то будет создан новый UUID) - * @return KktResponse|null + * @return AtolResponse|null * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -449,7 +449,7 @@ final class Receipt extends Entity * @throws InvalidPaymentAddressException * @throws TooLongPaymentAddressException */ - public function buyRefund(KktFiscalizer $fiscalizer, ?string $external_id = null): ?KktResponse + public function buyRefund(Fiscalizer $fiscalizer, ?string $external_id = null): ?AtolResponse { return $fiscalizer->buyRefund($this, $external_id); } diff --git a/src/Exceptions/AuthFailedException.php b/src/Exceptions/AuthFailedException.php index 61be470..6d2ab22 100644 --- a/src/Exceptions/AuthFailedException.php +++ b/src/Exceptions/AuthFailedException.php @@ -11,7 +11,7 @@ declare(strict_types = 1); namespace AtolOnline\Exceptions; -use AtolOnline\Api\KktResponse; +use AtolOnline\Api\AtolResponse; use Exception; use JetBrains\PhpStorm\Pure; @@ -23,11 +23,11 @@ class AuthFailedException extends Exception /** * Конструктор * - * @param KktResponse $response + * @param AtolResponse $response * @param string $message */ #[Pure] - public function __construct(KktResponse $response, string $message = '') + public function __construct(AtolResponse $response, string $message = '') { parent::__construct(($message ?: 'Ошибка авторизации: ') . ': ' . $response); } diff --git a/tests/AtolOnline/Tests/Api/KktFiscalizerTest.php b/tests/AtolOnline/Tests/Api/FiscalizerTest.php similarity index 68% rename from tests/AtolOnline/Tests/Api/KktFiscalizerTest.php rename to tests/AtolOnline/Tests/Api/FiscalizerTest.php index cee78db..07aa9ae 100644 --- a/tests/AtolOnline/Tests/Api/KktFiscalizerTest.php +++ b/tests/AtolOnline/Tests/Api/FiscalizerTest.php @@ -16,7 +16,7 @@ use AtolOnline\{ Tests\BasicTestCase}; use AtolOnline\Api\{ AtolClient, - KktFiscalizer}; + Fiscalizer}; use AtolOnline\Exceptions\{ AuthFailedException, EmptyCorrectionNumberException, @@ -39,8 +39,6 @@ use AtolOnline\Exceptions\{ TooHighPaymentSumException, TooLongCallbackUrlException, TooLongItemNameException, - TooLongLoginException, - TooLongPasswordException, TooLongPaymentAddressException, TooManyException}; use GuzzleHttp\Exception\GuzzleException; @@ -48,7 +46,7 @@ use GuzzleHttp\Exception\GuzzleException; /** * Набор тестов для проверки работы фискализатора */ -class KktFiscalizerTest extends BasicTestCase +class FiscalizerTest extends BasicTestCase { /** * @var array Массив UUID-ов результатов регистрации документов для переиспользования @@ -60,13 +58,13 @@ class KktFiscalizerTest extends BasicTestCase * Тестирует успешное создание объекта фискализатора без аргументов конструктора * * @return void - * @covers \AtolOnline\Api\KktFiscalizer + * @covers \AtolOnline\Api\Fiscalizer */ public function testConstructorWithourArgs(): void { - $fisc = new KktFiscalizer(); + $fisc = new Fiscalizer(); $this->assertIsObject($fisc); - $this->assertIsSameClass(KktFiscalizer::class, $fisc); + $this->assertIsSameClass(Fiscalizer::class, $fisc); $this->assertExtendsClasses([AtolClient::class], $fisc); } @@ -74,41 +72,41 @@ class KktFiscalizerTest extends BasicTestCase * Тестирует установку и возврат группы ККТ * * @return void - * @covers \AtolOnline\Api\KktFiscalizer - * @covers \AtolOnline\Api\KktFiscalizer::getGroup - * @covers \AtolOnline\Api\KktFiscalizer::setGroup + * @covers \AtolOnline\Api\Fiscalizer + * @covers \AtolOnline\Api\Fiscalizer::getGroup + * @covers \AtolOnline\Api\Fiscalizer::setGroup */ public function testGroup(): void { // test mode $this->assertEquals( TestEnvParams::FFD105()['group'], - (new KktFiscalizer(group: 'group'))->getGroup() + (new Fiscalizer(group: 'group'))->getGroup() ); // prod mode - $this->assertEquals('group', (new KktFiscalizer(false, group: 'group'))->getGroup()); - $this->assertNull((new KktFiscalizer(false))->getGroup()); + $this->assertEquals('group', (new Fiscalizer(false, group: 'group'))->getGroup()); + $this->assertNull((new Fiscalizer(false))->getGroup()); } /** * Тестирует выброс исключения при попытке передать пустую группу ККТ в конструктор * * @return void - * @covers \AtolOnline\Api\KktFiscalizer - * @covers \AtolOnline\Api\KktFiscalizer::setGroup + * @covers \AtolOnline\Api\Fiscalizer + * @covers \AtolOnline\Api\Fiscalizer::setGroup * @covers \AtolOnline\Exceptions\EmptyGroupException */ public function testEmptyGroupException(): void { $this->expectException(EmptyGroupException::class); - new KktFiscalizer(group: "\n\r \0\t"); + new Fiscalizer(group: "\n\r \0\t"); } /** * Тестирует выброс исключения при попытке установить слишком длинный адрес колбека * * @return void - * @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl + * @covers \AtolOnline\Api\Fiscalizer::setCallbackUrl * @covers \AtolOnline\Exceptions\TooLongCallbackUrlException * @throws InvalidCallbackUrlException * @throws TooLongCallbackUrlException @@ -116,14 +114,14 @@ class KktFiscalizerTest extends BasicTestCase public function testTooLongCallbackUrlException(): void { $this->expectException(TooLongCallbackUrlException::class); - (new KktFiscalizer())->setCallbackUrl(Helpers::randomStr(Constraints::MAX_LENGTH_CALLBACK_URL + 1)); + (new Fiscalizer())->setCallbackUrl(Helpers::randomStr(Constraints::MAX_LENGTH_CALLBACK_URL + 1)); } /** * Тестирует выброс исключения при попытке установить слишком длинный адрес колбека * * @return void - * @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl + * @covers \AtolOnline\Api\Fiscalizer::setCallbackUrl * @covers \AtolOnline\Exceptions\InvalidCallbackUrlException * @throws InvalidCallbackUrlException * @throws TooLongCallbackUrlException @@ -131,7 +129,7 @@ class KktFiscalizerTest extends BasicTestCase public function testInvalidCallbackUrlException(): void { $this->expectException(InvalidCallbackUrlException::class); - (new KktFiscalizer())->setCallbackUrl(Helpers::randomStr()); + (new Fiscalizer())->setCallbackUrl(Helpers::randomStr()); } /** @@ -139,15 +137,15 @@ class KktFiscalizerTest extends BasicTestCase * * @param mixed $param * @return void - * @covers \AtolOnline\Api\KktFiscalizer::setCallbackUrl - * @covers \AtolOnline\Api\KktFiscalizer::getCallbackUrl + * @covers \AtolOnline\Api\Fiscalizer::setCallbackUrl + * @covers \AtolOnline\Api\Fiscalizer::getCallbackUrl * @dataProvider providerNullableStrings * @throws InvalidCallbackUrlException * @throws TooLongCallbackUrlException */ public function testNullableCallbackUrl(mixed $param): void { - $this->assertNull((new KktFiscalizer())->setCallbackUrl($param)->getCallbackUrl()); + $this->assertNull((new Fiscalizer())->setCallbackUrl($param)->getCallbackUrl()); } /** @@ -155,11 +153,11 @@ class KktFiscalizerTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt::sell - * @covers \AtolOnline\Api\KktFiscalizer::sell - * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl - * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @covers \AtolOnline\Api\Fiscalizer::sell + * @covers \AtolOnline\Api\Fiscalizer::getFullUrl + * @covers \AtolOnline\Api\Fiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\Fiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\Fiscalizer::registerDocument * @throws AuthFailedException * @throws EmptyItemNameException * @throws EmptyItemsException @@ -175,16 +173,14 @@ class KktFiscalizerTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooHighPaymentSumException * @throws TooLongItemNameException - * @throws TooLongLoginException - * @throws TooLongPasswordException * @throws TooLongPaymentAddressException * @throws TooManyException * @throws GuzzleException */ public function testSell(): void { - $fisc_result = $this->newReceipt()->sell(new KktFiscalizer()); - $this->assertTrue($fisc_result->isValid()); + $fisc_result = $this->newReceipt()->sell(new Fiscalizer()); + $this->assertTrue($fisc_result->isSuccessful()); $this->assertEquals('wait', $fisc_result->getContent()->status); self::$registered_uuids[] = $fisc_result->getContent()->uuid; } @@ -194,11 +190,11 @@ class KktFiscalizerTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt::sellRefund - * @covers \AtolOnline\Api\KktFiscalizer::sellRefund - * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl - * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @covers \AtolOnline\Api\Fiscalizer::sellRefund + * @covers \AtolOnline\Api\Fiscalizer::getFullUrl + * @covers \AtolOnline\Api\Fiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\Fiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\Fiscalizer::registerDocument * @throws AuthFailedException * @throws EmptyItemNameException * @throws EmptyItemsException @@ -214,16 +210,14 @@ class KktFiscalizerTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooHighPaymentSumException * @throws TooLongItemNameException - * @throws TooLongLoginException - * @throws TooLongPasswordException * @throws TooLongPaymentAddressException * @throws TooManyException * @throws GuzzleException */ public function testSellRefund(): void { - $fisc_result = $this->newReceipt()->sellRefund(new KktFiscalizer()); - $this->assertTrue($fisc_result->isValid()); + $fisc_result = $this->newReceipt()->sellRefund(new Fiscalizer()); + $this->assertTrue($fisc_result->isSuccessful()); $this->assertEquals('wait', $fisc_result->getContent()->status); self::$registered_uuids[] = $fisc_result->getContent()->uuid; } @@ -233,11 +227,11 @@ class KktFiscalizerTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Correction::sellCorrect - * @covers \AtolOnline\Api\KktFiscalizer::sellCorrect - * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl - * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @covers \AtolOnline\Api\Fiscalizer::sellCorrect + * @covers \AtolOnline\Api\Fiscalizer::getFullUrl + * @covers \AtolOnline\Api\Fiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\Fiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\Fiscalizer::registerDocument * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -248,18 +242,16 @@ class KktFiscalizerTest extends BasicTestCase * @throws InvalidPaymentAddressException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException - * @throws TooLongLoginException - * @throws TooLongPasswordException * @throws TooLongPaymentAddressException * @throws EmptyCorrectionNumberException * @throws InvalidCorrectionDateException */ public function testSellCorrect(): void { - $fisc_result = $this->newCorrection()->sellCorrect(new KktFiscalizer()); - $this->assertTrue($fisc_result->isValid()); + $fisc_result = $this->newCorrection()->sellCorrect(new Fiscalizer()); + $this->assertTrue($fisc_result->isSuccessful()); $this->assertEquals('wait', $fisc_result->getContent()->status); - self::$registered_uuids[] = $fisc_result->getContent()->uuid; + //self::$registered_uuids[] = $fisc_result->getContent()->uuid; } /** @@ -267,11 +259,11 @@ class KktFiscalizerTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt::buy - * @covers \AtolOnline\Api\KktFiscalizer::buy - * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl - * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @covers \AtolOnline\Api\Fiscalizer::buy + * @covers \AtolOnline\Api\Fiscalizer::getFullUrl + * @covers \AtolOnline\Api\Fiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\Fiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\Fiscalizer::registerDocument * @throws AuthFailedException * @throws EmptyItemNameException * @throws EmptyItemsException @@ -287,18 +279,16 @@ class KktFiscalizerTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooHighPaymentSumException * @throws TooLongItemNameException - * @throws TooLongLoginException - * @throws TooLongPasswordException * @throws TooLongPaymentAddressException * @throws TooManyException * @throws GuzzleException */ public function testBuy(): void { - $fisc_result = $this->newReceipt()->buy(new KktFiscalizer()); - $this->assertTrue($fisc_result->isValid()); + $fisc_result = $this->newReceipt()->buy(new Fiscalizer()); + $this->assertTrue($fisc_result->isSuccessful()); $this->assertEquals('wait', $fisc_result->getContent()->status); - self::$registered_uuids[] = $fisc_result->getContent()->uuid; + //self::$registered_uuids[] = $fisc_result->getContent()->uuid; } /** @@ -306,11 +296,11 @@ class KktFiscalizerTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Receipt::buyRefund - * @covers \AtolOnline\Api\KktFiscalizer::buyRefund - * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl - * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @covers \AtolOnline\Api\Fiscalizer::buyRefund + * @covers \AtolOnline\Api\Fiscalizer::getFullUrl + * @covers \AtolOnline\Api\Fiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\Fiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\Fiscalizer::registerDocument * @throws AuthFailedException * @throws EmptyItemNameException * @throws EmptyItemsException @@ -326,18 +316,16 @@ class KktFiscalizerTest extends BasicTestCase * @throws TooHighItemPriceException * @throws TooHighPaymentSumException * @throws TooLongItemNameException - * @throws TooLongLoginException - * @throws TooLongPasswordException * @throws TooLongPaymentAddressException * @throws TooManyException * @throws GuzzleException */ public function testBuyRefund(): void { - $fisc_result = $this->newReceipt()->buyRefund(new KktFiscalizer()); - $this->assertTrue($fisc_result->isValid()); + $fisc_result = $this->newReceipt()->buyRefund(new Fiscalizer()); + $this->assertTrue($fisc_result->isSuccessful()); $this->assertEquals('wait', $fisc_result->getContent()->status); - self::$registered_uuids[] = $fisc_result->getContent()->uuid; + //self::$registered_uuids[] = $fisc_result->getContent()->uuid; } /** @@ -345,11 +333,11 @@ class KktFiscalizerTest extends BasicTestCase * * @return void * @covers \AtolOnline\Entities\Correction::buyCorrect - * @covers \AtolOnline\Api\KktFiscalizer::buyCorrect - * @covers \AtolOnline\Api\KktFiscalizer::getFullUrl - * @covers \AtolOnline\Api\KktFiscalizer::getAuthEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::getMainEndpoint - * @covers \AtolOnline\Api\KktFiscalizer::registerDocument + * @covers \AtolOnline\Api\Fiscalizer::buyCorrect + * @covers \AtolOnline\Api\Fiscalizer::getFullUrl + * @covers \AtolOnline\Api\Fiscalizer::getAuthEndpoint + * @covers \AtolOnline\Api\Fiscalizer::getMainEndpoint + * @covers \AtolOnline\Api\Fiscalizer::registerDocument * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -360,37 +348,34 @@ class KktFiscalizerTest extends BasicTestCase * @throws InvalidPaymentAddressException * @throws NegativePaymentSumException * @throws TooHighPaymentSumException - * @throws TooLongLoginException - * @throws TooLongPasswordException * @throws TooLongPaymentAddressException * @throws EmptyCorrectionNumberException * @throws InvalidCorrectionDateException */ public function testBuyCorrect(): void { - $fisc_result = $this->newCorrection()->buyCorrect(new KktFiscalizer()); - $this->assertTrue($fisc_result->isValid()); + $fisc_result = $this->newCorrection()->buyCorrect(new Fiscalizer()); + $this->assertTrue($fisc_result->isSuccessful()); $this->assertEquals('wait', $fisc_result->getContent()->status); - self::$registered_uuids[] = $fisc_result->getContent()->uuid; + //self::$registered_uuids[] = $fisc_result->getContent()->uuid; } /** * Тестирует разовое получение статуса фискализации документа * * @return void - * @covers \AtolOnline\Api\KktFiscalizer::getDocumentStatus + * @covers \AtolOnline\Api\Fiscalizer::getDocumentStatus + * @depends testSell * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws GuzzleException * @throws InvalidUuidException - * @throws TooLongLoginException - * @throws TooLongPasswordException */ public function testGetDocumentStatus(): void { - $fisc_status = (new KktFiscalizer())->getDocumentStatus(self::$registered_uuids[0]); - $this->assertTrue($fisc_status->isValid()); + $fisc_status = (new Fiscalizer())->getDocumentStatus(array_shift(self::$registered_uuids)); + $this->assertTrue($fisc_status->isSuccessful()); $this->assertTrue(in_array($fisc_status->getContent()->status, ['wait', 'done'])); } @@ -398,20 +383,18 @@ class KktFiscalizerTest extends BasicTestCase * Тестирует опрос API на получение статуса фискализации документа * * @return void - * @covers \AtolOnline\Api\KktFiscalizer::pollDocumentStatus + * @covers \AtolOnline\Api\Fiscalizer::pollDocumentStatus + * @depends testSellRefund * @throws AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException * @throws GuzzleException * @throws InvalidUuidException - * @throws TooLongLoginException - * @throws TooLongPasswordException */ public function testPollDocumentStatus(): void { - $fisc_status = (new KktFiscalizer())->pollDocumentStatus(self::$registered_uuids[1]); - $this->assertTrue($fisc_status->isValid()); + $fisc_status = (new Fiscalizer())->pollDocumentStatus(array_shift(self::$registered_uuids)); + $this->assertTrue($fisc_status->isSuccessful()); $this->assertEquals('done', $fisc_status->getContent()->status); } - } diff --git a/tests/AtolOnline/Tests/Api/KktMonitorTest.php b/tests/AtolOnline/Tests/Api/MonitorTest.php similarity index 74% rename from tests/AtolOnline/Tests/Api/KktMonitorTest.php rename to tests/AtolOnline/Tests/Api/MonitorTest.php index 56b7753..bebe25c 100644 --- a/tests/AtolOnline/Tests/Api/KktMonitorTest.php +++ b/tests/AtolOnline/Tests/Api/MonitorTest.php @@ -10,8 +10,8 @@ namespace AtolOnline\Tests\Api; use AtolOnline\Api\AtolClient; -use AtolOnline\Api\KktMonitor; -use AtolOnline\Api\KktResponse; +use AtolOnline\Api\AtolResponse; +use AtolOnline\Api\Monitor; use AtolOnline\Entities\Kkt; use AtolOnline\Exceptions\AuthFailedException; use AtolOnline\Exceptions\EmptyLoginException; @@ -29,20 +29,20 @@ use GuzzleHttp\Exception\GuzzleException; /** * Набор тестов для проверки работы API-клиента на примере монитора ККТ */ -class KktMonitorTest extends BasicTestCase +class MonitorTest extends BasicTestCase { /** * Возвращает объект монитора для тестирования с тестовым API АТОЛ * - * @return KktMonitor + * @return Monitor * @throws EmptyLoginException * @throws EmptyPasswordException * @throws TooLongLoginException * @throws TooLongPasswordException */ - private function newTestClient(): KktMonitor + private function newTestClient(): Monitor { - return (new KktMonitor(true)) + return (new Monitor(true)) ->setLogin(TestEnvParams::FFD105()['login']) ->setPassword(TestEnvParams::FFD105()['password']); } @@ -50,24 +50,24 @@ class KktMonitorTest extends BasicTestCase /** * Тестирует успешное создание объекта монитора без аргументов конструктора * - * @covers \AtolOnline\Api\KktMonitor::__construct + * @covers \AtolOnline\Api\Monitor::__construct */ public function testConstructorWithoutArgs(): void { - $client = new KktMonitor(); + $client = new Monitor(); $this->assertIsObject($client); - $this->assertIsSameClass(KktMonitor::class, $client); + $this->assertIsSameClass(Monitor::class, $client); $this->assertExtendsClasses([AtolClient::class], $client); } /** * Тестирует успешное создание объекта монитора с аргументами конструктора * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::setLogin - * @covers \AtolOnline\Api\KktMonitor::getLogin - * @covers \AtolOnline\Api\KktMonitor::setPassword - * @covers \AtolOnline\Api\KktMonitor::getPassword + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::setLogin + * @covers \AtolOnline\Api\Monitor::getLogin + * @covers \AtolOnline\Api\Monitor::setPassword + * @covers \AtolOnline\Api\Monitor::getPassword * @throws EmptyLoginException * @throws EmptyPasswordException * @throws TooLongLoginException @@ -75,27 +75,28 @@ class KktMonitorTest extends BasicTestCase */ public function testConstructorWithArgs(): void { - $client = new KktMonitor(false, 'login', 'password', []); + $client = new Monitor(false, 'login', 'password', []); $this->assertIsObject($client); - $this->assertIsSameClass($client, KktMonitor::class); + $this->assertIsSameClass($client, Monitor::class); $this->assertExtendsClasses([AtolClient::class], $client); } + // /** * Тестирует установку и возврат логина * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::getLogin - * @covers \AtolOnline\Api\KktMonitor::setLogin + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::getLogin + * @covers \AtolOnline\Api\Monitor::setLogin * @throws EmptyLoginException * @throws TooLongLoginException */ public function testLogin(): void { - $client = new KktMonitor(false, login: 'login'); + $client = new Monitor(false, login: 'login'); $this->assertEquals('login', $client->getLogin()); - $client = new KktMonitor(); + $client = new Monitor(); $this->assertEquals(TestEnvParams::FFD105()['login'], $client->getLogin()); $client->setLogin('login'); @@ -105,21 +106,21 @@ class KktMonitorTest extends BasicTestCase /** * Тестирует исключение при попытке передать пустой логин в конструктор * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::setLogin + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::setLogin * @covers \AtolOnline\Exceptions\EmptyLoginException */ public function testEmptyLoginException(): void { $this->expectException(EmptyLoginException::class); - new KktMonitor(login: ''); + new Monitor(login: ''); } /** * Тестирует исключение при попытке передать слишком длинный логин в конструктор * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::setLogin + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::setLogin * @covers \AtolOnline\Exceptions\TooLongLoginException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -129,24 +130,24 @@ class KktMonitorTest extends BasicTestCase public function testTooLongLoginException(): void { $this->expectException(TooLongLoginException::class); - new KktMonitor(login: Helpers::randomStr(101)); + new Monitor(login: Helpers::randomStr(101)); } /** * Тестирует установку и возврат пароля * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::getPassword - * @covers \AtolOnline\Api\KktMonitor::setPassword + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::getPassword + * @covers \AtolOnline\Api\Monitor::setPassword * @throws EmptyPasswordException * @throws TooLongPasswordException */ public function testPassword(): void { - $client = new KktMonitor(false, password: 'password'); + $client = new Monitor(false, password: 'password'); $this->assertEquals('password', $client->getPassword()); - $client = new KktMonitor(); + $client = new Monitor(); $this->assertEquals(TestEnvParams::FFD105()['password'], $client->getPassword()); $client->setPassword('password'); @@ -156,21 +157,21 @@ class KktMonitorTest extends BasicTestCase /** * Тестирует исключение при попытке передать пустой пароль в конструктор * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::setPassword + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::setPassword * @covers \AtolOnline\Exceptions\EmptyPasswordException */ public function testEmptyPasswordException(): void { $this->expectException(EmptyPasswordException::class); - new KktMonitor(password: ''); + new Monitor(password: ''); } /** * Тестирует исключение при попытке передать слишком длинный пароль в конструктор * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::setPassword + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::setPassword * @throws EmptyLoginException * @throws EmptyPasswordException * @throws TooLongLoginException @@ -179,34 +180,34 @@ class KktMonitorTest extends BasicTestCase public function testConstructorWithLongPassword(): void { $this->expectException(TooLongPasswordException::class); - new KktMonitor(password: Helpers::randomStr(101)); + new Monitor(password: Helpers::randomStr(101)); } /** * Тестирует установку тестового режима * - * @covers \AtolOnline\Api\KktMonitor::__construct - * @covers \AtolOnline\Api\KktMonitor::isTestMode - * @covers \AtolOnline\Api\KktMonitor::setTestMode + * @covers \AtolOnline\Api\Monitor::__construct + * @covers \AtolOnline\Api\Monitor::isTestMode + * @covers \AtolOnline\Api\Monitor::setTestMode */ public function testTestMode(): void { - $client = new KktMonitor(); + $client = new Monitor(); $this->assertTrue($client->isTestMode()); - $client = new KktMonitor(true); + $client = new Monitor(true); $this->assertTrue($client->isTestMode()); - $client = new KktMonitor(false); + $client = new Monitor(false); $this->assertFalse($client->isTestMode()); - $client = (new KktMonitor())->setTestMode(); + $client = (new Monitor())->setTestMode(); $this->assertTrue($client->isTestMode()); - $client = (new KktMonitor())->setTestMode(true); + $client = (new Monitor())->setTestMode(true); $this->assertTrue($client->isTestMode()); - $client = (new KktMonitor())->setTestMode(false); + $client = (new Monitor())->setTestMode(false); $this->assertFalse($client->isTestMode()); } @@ -214,10 +215,10 @@ class KktMonitorTest extends BasicTestCase * Тестирует авторизацию * * @covers \AtolOnline\Api\AtolClient::getHeaders - * @covers \AtolOnline\Api\KktMonitor::sendRequest - * @covers \AtolOnline\Api\KktMonitor::getAuthEndpoint - * @covers \AtolOnline\Api\KktMonitor::doAuth - * @covers \AtolOnline\Api\KktMonitor::auth + * @covers \AtolOnline\Api\Monitor::sendRequest + * @covers \AtolOnline\Api\Monitor::getAuthEndpoint + * @covers \AtolOnline\Api\Monitor::doAuth + * @covers \AtolOnline\Api\Monitor::auth * @covers \AtolOnline\Exceptions\AuthFailedException * @throws EmptyLoginException * @throws EmptyPasswordException @@ -237,8 +238,8 @@ class KktMonitorTest extends BasicTestCase * Тестирует возврат токена после авторизации * * @depends testAuth - * @covers \AtolOnline\Api\KktMonitor::setToken - * @covers \AtolOnline\Api\KktMonitor::getToken + * @covers \AtolOnline\Api\Monitor::setToken + * @covers \AtolOnline\Api\Monitor::getToken * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException @@ -249,7 +250,7 @@ class KktMonitorTest extends BasicTestCase */ public function testGetToken(): void { - $client = new KktMonitor(); + $client = new Monitor(); $this->assertNull($client->getToken()); $this->skipIfMonitoringIsOffline(); @@ -262,7 +263,7 @@ class KktMonitorTest extends BasicTestCase * Тестирует возврат объекта последнего ответа от API * * @depends testAuth - * @covers \AtolOnline\Api\KktMonitor::getLastResponse + * @covers \AtolOnline\Api\Monitor::getLastResponse * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException @@ -276,17 +277,17 @@ class KktMonitorTest extends BasicTestCase $this->skipIfMonitoringIsOffline(); $client = $this->newTestClient(); $client->auth(); - $this->assertIsSameClass(KktResponse::class, $client->getLastResponse()); + $this->assertIsSameClass(AtolResponse::class, $client->getLastResponse()); } /** * [Мониторинг] Тестирует получение данных о всех ККТ * * @depends testAuth - * @covers \AtolOnline\Api\KktMonitor::getMainEndpoint + * @covers \AtolOnline\Api\Monitor::getMainEndpoint * @covers \AtolOnline\Api\AtolClient::getUrlToMethod - * @covers \AtolOnline\Api\KktMonitor::fetchAll - * @covers \AtolOnline\Api\KktMonitor::getAll + * @covers \AtolOnline\Api\Monitor::fetchAll + * @covers \AtolOnline\Api\Monitor::getAll * @covers \AtolOnline\Exceptions\AuthFailedException * @throws AuthFailedException * @throws EmptyLoginException @@ -312,10 +313,10 @@ class KktMonitorTest extends BasicTestCase * [Мониторинг] Тестирует получение данных о конкретной ККТ * * @depends testAuth - * @covers \AtolOnline\Api\KktMonitor::getMainEndpoint + * @covers \AtolOnline\Api\Monitor::getMainEndpoint * @covers \AtolOnline\Api\AtolClient::getUrlToMethod - * @covers \AtolOnline\Api\KktMonitor::fetchOne - * @covers \AtolOnline\Api\KktMonitor::getOne + * @covers \AtolOnline\Api\Monitor::fetchOne + * @covers \AtolOnline\Api\Monitor::getOne * @covers \AtolOnline\Entities\Kkt::__construct * @covers \AtolOnline\Entities\Kkt::__get * @covers \AtolOnline\Entities\Kkt::jsonSerialize From 58bc344a86a69c4436c5f88b2f29d49e8eef99d1 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 19 Dec 2021 22:30:21 +0800 Subject: [PATCH 82/85] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D0=B5=D0=B5=20=D0=B8?= =?UTF-8?q?=D0=BB=D0=B8=20=D0=BC=D0=B5=D0=BD=D0=B5=D0=B5=20=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D0=B0=D1=8F=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/client.md | 67 --------- docs/collection.md | 19 +++ docs/company.md | 67 --------- docs/correction_info.md | 62 -------- docs/documents.md | 167 --------------------- docs/entity.md | 52 +++++++ docs/fiscalizing.md | 237 ++++++++++++++++++++++++++++++ docs/items.md | 202 ------------------------- docs/kkt.md | 317 ---------------------------------------- docs/monitoring.md | 14 +- docs/payments.md | 134 ----------------- docs/readme.md | 64 ++++---- docs/response.md | 57 ++++++++ docs/vats.md | 128 ---------------- 14 files changed, 405 insertions(+), 1182 deletions(-) delete mode 100644 docs/client.md create mode 100644 docs/collection.md delete mode 100644 docs/company.md delete mode 100644 docs/correction_info.md delete mode 100644 docs/documents.md create mode 100644 docs/entity.md create mode 100644 docs/fiscalizing.md delete mode 100644 docs/items.md delete mode 100644 docs/kkt.md delete mode 100644 docs/payments.md create mode 100644 docs/response.md delete mode 100644 docs/vats.md diff --git a/docs/client.md b/docs/client.md deleted file mode 100644 index 73072ed..0000000 --- a/docs/client.md +++ /dev/null @@ -1,67 +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', // наименование - 'john@example.com', // email - '+1/22/99*73s dsdas654 5s6', // номер телефона +122997365456 - '+fasd3\qe3fs_=nac990139928czc' // номер ИНН 3399013928 -); - -// 2 способ - через сеттеры -$customer = (new AtolOnline\Entities\Client()) - ->setEmail('john@example.com') - ->setInn('+fasd3\q3fs_=nac9901 3928c-c') - ->setName('John Doe') - ->setPhone('+1/22/99*73s dsdas654 5s6'); - -// либо комбинация этих способов -``` - -Получить установленные значения атрибутов можно через геттеры: - -```php -$customer->getInn(); -$customer->getEmail(); -$customer->getName(); -$customer->getPhone(); -``` - -Объект класса приводится к JSON-строке автоматически или принудительно: - -```php -echo $customer; -$json_string = (string)$customer; -``` - -Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`: - -```php -$json_array = $customer->jsonSerialize(); -``` - ---- - -[Вернуться к содержанию](readme.md) diff --git a/docs/collection.md b/docs/collection.md new file mode 100644 index 0000000..1c0a8b6 --- /dev/null +++ b/docs/collection.md @@ -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) diff --git a/docs/company.md b/docs/company.md deleted file mode 100644 index d13305e..0000000 --- a/docs/company.md +++ /dev/null @@ -1,67 +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( - 'company@example.com' // email - AtolOnline\Constants\SnoTypes::OSN, // тип СНО - '5544332219', // номер ИНН - 'https://v4.online.atol.ru', // адрес места расчётов -); - -// 2 способ - через сеттеры -$company - ->setEmail('company@example.com') - ->setInn('5544332219') - ->setSno(AtolOnline\Constants\SnoTypes::USN_INCOME) - ->setPaymentAddress('https://v4.online.atol.ru'); - -// либо комбинация этих способов -``` - -Получить установленные значения параметров можно через геттеры: - -```php -$company->getInn(); -$company->getEmail(); -$company->getPaymentAddress(); -$company->getSno(); -``` - -Объект класса приводится к JSON-строке автоматически или принудительно: - -```php -echo $company; -$json_string = (string)$company; -``` - -Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`: - -```php -$json_array = $company->jsonSerialize(); -``` - ---- - -[Вернуться к содержанию](readme.md) \ No newline at end of file diff --git a/docs/correction_info.md b/docs/correction_info.md deleted file mode 100644 index c90cade..0000000 --- a/docs/correction_info.md +++ /dev/null @@ -1,62 +0,0 @@ -# Работа с данными коррекции - -[Вернуться к содержанию](readme.md) - ---- - -Объект для данных коррекции инициализируется следующим образом: - -```php -$info = new AtolOnline\Entities\CorrectionInfo(); -``` - -У объекта должны быть указаны все следующие обязательные атрибуты: -* тип коррекции (тег ФФД 1173) - все типы перечислены в классе `AtolOnline\Constants\CorrectionTypes`; -* дата документа основания для коррекции в формате `d.m.Y` (тег ФФД 1178); -* номер документа основания для коррекции (тег ФФД 1179). - -Указать эти атрибуты можно двумя способами: - -```php -use AtolOnline\{Entities\CorrectionInfo, Constants\CorrectionTypes}; - -// 1 способ - через конструктор -$info = new CorrectionInfo( - CorrectionTypes::SELF, // тип коррекции - '01.01.2019', // дата документа коррекции - '12345', // номер документа коррекции -); - -// 2 способ - через сеттеры -$info = (new CorrectionInfo()) - ->setType(CorrectionTypes::INSTRUCTION) - ->setDate('01.01.2019') - ->setNumber('9999'); - -// либо комбинация этих способов -``` - -Получить установленные значения атрибутов можно через геттеры: - -```php -$info->getType(); -$info->getDate(); -$info->getNumber(); -``` - -Объект класса приводится к JSON-строке автоматически или принудительным приведением к `string`: - -```php -echo $customer; -$json_string = (string)$customer; -``` - -Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`: - -```php -$json_array = $customer->jsonSerialize(); -``` - ---- - -[Вернуться к содержанию](readme.md) \ No newline at end of file diff --git a/docs/documents.md b/docs/documents.md deleted file mode 100644 index 1d92174..0000000 --- a/docs/documents.md +++ /dev/null @@ -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()`. - -Всё в рублях. - - -## Работа с данными коррекции - -Если документ создаётся с целью коррекции прихода или расхода, то он обязательно должен содержать [данные коррекции](/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) diff --git a/docs/entity.md b/docs/entity.md new file mode 100644 index 0000000..76c79bc --- /dev/null +++ b/docs/entity.md @@ -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) diff --git a/docs/fiscalizing.md b/docs/fiscalizing.md new file mode 100644 index 0000000..042d4be --- /dev/null +++ b/docs/fiscalizing.md @@ -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); +``` + + + +## Тестовый режим + +По умолчанию фискализатор создаётся для работы в тестовом режиме. Это означает, что работа с АТОЛ Онлайн 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) diff --git a/docs/items.md b/docs/items.md deleted file mode 100644 index 730a1e8..0000000 --- a/docs/items.md +++ /dev/null @@ -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(); -``` - - -## Массив объектов предметов расчёта - -> Максимальное количество объектов в массиве - 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) \ No newline at end of file diff --git a/docs/kkt.md b/docs/kkt.md deleted file mode 100644 index aae3c72..0000000 --- a/docs/kkt.md +++ /dev/null @@ -1,317 +0,0 @@ -# Работа с ККТ - -[Вернуться к содержанию](readme.md) - ---- - -## Доступ к ККТ - -Для работы с облачной ККТ необходимы следующие параметры: - -* логин; -* пароль; -* код группы. - -Чтоы получить их, нужно: - -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\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), который будет передаваться в документах через эту ККТ. - - -## Тестовый режим - -На самом деле, в АТОЛ Онлайн нет понятия *тестовая операция* или чего-то в этом духе. -АТОЛ предоставляет нам отдельную тестовую среду (ККТ). -[Её настройки](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) diff --git a/docs/monitoring.md b/docs/monitoring.md index c3dcf2f..3bb81ce 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -1,6 +1,6 @@ # Мониторинг ККТ -[Вернуться к содержанию](readme.md) +[Вернуться к содержанию](readme.md#toc) --- @@ -12,13 +12,13 @@ ```php // можно передать параметры подключения в конструктор -$monitor = new AtolOnline\Api\KktMonitor( +$monitor = new AtolOnline\Api\Monitor( login: 'mylogin', password: 'qwerty' ); // можно - отдельными сеттерами -$monitor = new AtolOnline\Api\KktMonitor(); +$monitor = new AtolOnline\Api\Monitor(); ->setLogin($credentials['login']) ->setPassword($credentials['password']); ``` @@ -30,7 +30,7 @@ $monitor = new AtolOnline\Api\KktMonitor(); ```php // передачей в конструктор `false` первым параметром: -$monitor = new AtolOnline\Api\KktMonitor(false, /*...*/); +$monitor = new AtolOnline\Api\Monitor(false, /*...*/); // или отдельным сеттером $monitor->setTestMode(false); @@ -107,10 +107,12 @@ $kkt = $monitor->getOne($kkts->first()->serialNumber); Класс `AtolOnline\Api\KktMonitor` расширяет абстрактный класс `AtolOnline\Api\AtolClient`. -Это значит, что последний ответ от API АТОЛ всегда сохраняется объектом класса `AtolOnline\Api\KktResponse`. К нему +Это значит, что последний ответ от API АТОЛ всегда сохраняется объектом класса `AtolOnline\Api\AtolReponse`. К нему можно обратиться через метод `AtolOnline\Api\KktMonitor::getResponse()`, независимо от того, что возвращают другие методы монитора. --- -[Вернуться к содержанию](readme.md) +Читай также: [Обработка ответа API](response.md) + +[Вернуться к содержанию](readme.md#toc) diff --git a/docs/payments.md b/docs/payments.md deleted file mode 100644 index 9829a57..0000000 --- a/docs/payments.md +++ /dev/null @@ -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(); -``` - - -## Массив объектов оплат - -> Максимальное количество объектов в массиве - 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) \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md index 53dc201..9b95680 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,26 +1,42 @@ # Документация к библиотеке + + +## Содержание + +* [Общий алгоритм](#getstarted) +* [Сущность](entity.md) +* [Коллекция сущностей](collection.md) +* [Мониторинг ККТ](monitoring.md) +* [Фискализация документа](fiscalizing.md) +* [Обработка ответа API](response.md) + +Если вы нашли опечатку или какое-то несоответствие — делайте pull-request. + + + ## Общий алгоритм -1. Задать настройки ККТ -2. Собрать данные о покупателе -3. Собрать данные о продавце -4. Собрать данные о предметах расчёта (товары, услуги и пр.) -5. Создать документ, добавив в него покупателя, продавца и предметы расчёта -6. Отправить документ на регистрацию: - 6.1. *Необязательно:* задать `callback_url`, на который АТОЛ отправит HTTP POST о состоянии документа. -7. Запомнить `uuid` из пришедшего ответа, поскольку он пригодится для последующей проверки статуса фискализации. - > Если с документом был передан `callback_url`, то ответ придёт на этот самый URL. - Если с документом **НЕ** был передан `callback_url` **либо** callback от АТОЛа не пришёл в течение 300 секунд (5 минут), нужно запрашивать вручную по `uuid`, пришедшему от АТОЛа в ответ на регистрацию документа. -8. Проверить состояние документа: - 8.1. взять `uuid` ответа, полученного на запрос фискализации; - 8.2. отправить его в запросе состояния документа. +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 суток после успешной фискализации. -В зависимости от специфики бизнеса, в документах можно/нужно передавать также и другую информацию. Подробности в -документациях и исходниках. +В зависимости от специфики бизнеса, в документах можно/нужно передавать разную информацию. Подробности в документации +АТОЛ Онлайн и исходниках библиотеки. -### Об отправке электронного чека покупателю +## Об отправке электронного чека покупателю После успешной фискализации документа покупатель автоматически получит уведомление **от ОФД**, который используется в связке с вашей ККТ: @@ -31,19 +47,3 @@ * если на стороне ОФД необходима и подключена услуга СМС-информирования (уточняйте подробности о своего ОФД). > Если заданы email и телефон, то ОФД отдаёт приоритет email. - -## Подготовка документа - -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) -9. [Мониторинг ККТ](monitoring.md) - ---- - -Если вы нашли опечатку или какое-то несоответствие — делайте pull-request. diff --git a/docs/response.md b/docs/response.md new file mode 100644 index 0000000..f13ec8d --- /dev/null +++ b/docs/response.md @@ -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) diff --git a/docs/vats.md b/docs/vats.md deleted file mode 100644 index 371a447..0000000 --- a/docs/vats.md +++ /dev/null @@ -1,128 +0,0 @@ -# Работа со ставками НДС - -[Вернуться к содержанию](readme.md) - ---- - -## Один объект - -Объект ставки НДС инициализируется следующим образом: - -```php -use AtolOnline\Entities\Vat; -use AtolOnline\Enums\VatTypes; - -$vat = new Vat( - VatTypes::VAT10, // тип ставки - 123.45 // сумма в рублях, от которой считать ставку -); -``` - -Для типа и суммы имеются отдельные сеттеры: - -```php -$vat->setType(VatTypes::VAT20) - ->setSum(100.15); // 123.45 заменится на 100.15 -``` - -Общую сумму, из которой расчитывается размер налога, можно увеличить, используя метод `addSum()`. Указанная в рублях -сумма увеличится на указанные рубли. Для уменьшения суммы следует передать отрицательное число. - -```php -$vat->addSum(40) // 100.15 + 40 = 140.15р - ->addSum(-15); // 140.15 - 15 = 125.15р -``` - -Получить установленную сумму можно через геттер `getSum()`: - -```php -$vat->getSum(); // 125.15р -``` - -Размер налога по ставке высчитывается из этой общей суммы. Не смотря на то, что геттер и сеттер работают с рублями, ** -расчёты производятся в копейках**. Сделать это можно через `getCalculated()`: - -```php -$vat->getCalculated(); -// для примера выше это 20% от 125.15р = 25.03р -``` - -Разберём комплексный пример изменения типа ставки и расчётной суммы: - -Объект класса приводится к JSON-строке автоматически или принудительно: - -```php -echo $vat; -$json_string = (string)$vat; -``` - -Чтобы получить те же данные в виде массива, нужно вызвать метод `jsonSerialize()`: - -```php -$json_array = $vat->jsonSerialize(); -``` - - -## Массив объектов ставок НДС - -> Максимальное количество в массиве - 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) From e22c1cb091f063152aed9cb0df34ac5995a7b5d7 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 19 Dec 2021 22:50:52 +0800 Subject: [PATCH 83/85] =?UTF-8?q?=D0=A4=D0=B8=D0=BD=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BE=D1=81=D1=82=D0=B0=D0=B2?= =?UTF-8?q?=D1=88=D0=B8=D1=85=D1=81=D1=8F=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Api/AtolResponse.php | 5 ++--- src/Api/Fiscalizer.php | 2 +- src/Api/Monitor.php | 2 +- src/Entities/Correction.php | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Api/AtolResponse.php b/src/Api/AtolResponse.php index 41fb3f1..5d7739d 100644 --- a/src/Api/AtolResponse.php +++ b/src/Api/AtolResponse.php @@ -15,8 +15,7 @@ namespace AtolOnline\Api; use JetBrains\PhpStorm\{ ArrayShape, - Pure -}; + Pure}; use JsonSerializable; use Psr\Http\Message\ResponseInterface; use Stringable; @@ -27,7 +26,7 @@ use Stringable; * @property mixed $error * @package AtolOnline\Api */ -class AtolResponse implements JsonSerializable, Stringable +final class AtolResponse implements JsonSerializable, Stringable { /** * @var int Код ответа сервера diff --git a/src/Api/Fiscalizer.php b/src/Api/Fiscalizer.php index 8bcbe78..17c90b5 100644 --- a/src/Api/Fiscalizer.php +++ b/src/Api/Fiscalizer.php @@ -38,7 +38,7 @@ use Ramsey\Uuid\Uuid; /** * Класс фискализатора для регистрации документов на ККТ */ -class Fiscalizer extends AtolClient +final class Fiscalizer extends AtolClient { /** * @var string|null Группа ККТ diff --git a/src/Api/Monitor.php b/src/Api/Monitor.php index 4f2eea0..77ecaf8 100644 --- a/src/Api/Monitor.php +++ b/src/Api/Monitor.php @@ -27,7 +27,7 @@ use JetBrains\PhpStorm\Pure; * * @see https://online.atol.ru/files/API_service_information.pdf Документация */ -class Monitor extends AtolClient +final class Monitor extends AtolClient { /** * @inheritDoc diff --git a/src/Entities/Correction.php b/src/Entities/Correction.php index b9e4a3d..3e8fade 100644 --- a/src/Entities/Correction.php +++ b/src/Entities/Correction.php @@ -33,7 +33,7 @@ use JetBrains\PhpStorm\ArrayShape; * * @see https://online.atol.ru/files/API_atol_online_v4.pdf Документация, стр 35 */ -class Correction extends Entity +final class Correction extends Entity { /** * Тип документа @@ -188,7 +188,7 @@ class Correction extends Entity * * @return Vats|null */ - public function getVats(): ?Vats + public function getVats(): Vats { return $this->vats ?? new Vats(); } @@ -200,7 +200,7 @@ class Correction extends Entity * @return $this * @throws Exception */ - public function setVats(?Vats $vats): self + public function setVats(Vats $vats): self { $vats->checkCount(); $vats->checkItemsClasses(); From d7f3c81fac96510f9c3c06154940b7b46c16bcb9 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 19 Dec 2021 22:52:21 +0800 Subject: [PATCH 84/85] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=20gh-actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 766c2fe..04162f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: push: branches: [ master, dev ] pull_request: - branches: [ master, dev ] + branches: [ dev ] jobs: Tests: From 2e8099e0a45a2aa4afd5ce2b56fa510c0c087a65 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Sun, 19 Dec 2021 22:58:47 +0800 Subject: [PATCH 85/85] =?UTF-8?q?=D0=90=D0=BA=D1=82=D1=83=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 385ee04..b29805b 100644 --- a/README.md +++ b/README.md @@ -6,36 +6,30 @@ [![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/). +Библиотека для фискализации чеков по 54-ФЗ через [облачные ККТ АТОЛ](https://online.atol.ru/). **[Документация](/docs/readme.md)** ---- +Текущие поддерживаемые версии АТОЛ Онлайн: -**В ветке `dev` проводится глубокий рефакторинг, стабилизация и активная подготовка к `v1.0.0`. -Документация актуализируется постепенно.** +| Протокол | 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) | -Текущие поддерживаемые версии АТОЛ Онлайн: - -| Протокол | API | ФФД | Статус | -|----------|-----|------|-------------| -| v4 | 5.8 | 1.05 | Рефакторинг | -| v5 | 2.0 | 1.2 | В планах | - ## Плюшечки * Мониторинг ККТ и ФН * Фискализация докумнетов на облачной ККТ * Валидация данных до отправки документа на ККТ (насколько это возможно, согласно схеме) * Расчёты денег в копейках -* PSR-4 автозагрузка - +* PSR-4 автозагрузка, покрытие настоящими тестами, fluent-setters ## Системные требования @@ -51,7 +45,7 @@ ### Подключение библиотеки -1. Установить библиотеку пакет к проекту: +1. Подключить пакет к проекту: ```bash composer require axenov/atol-online ``` @@ -70,10 +64,10 @@ ```bash composer test # обычное тестирование -composer test-cov # тестирование с покрытием +composer coverage # тестирование с покрытием ``` -После тестирования с покрытием создаётся отчёт в директории `.coverage-report` в корне репозитория. +После тестирования с покрытием создаётся отчёт в директории `.coverage` в корне репозитория. ## Использование библиотеки @@ -90,8 +84,6 @@ composer test-cov # тестирование с покрытием ## Дополнительные ресурсы -* **[Документация к библиотеке](/docs/readme.md)** -* Telegram-канал: [@atolonline_php](https://t.me/atolonline_php) * [Документация АТОЛ Онлайн](https://online.atol.ru/lib/) ## Лицензия