diff --git a/.github/workflows/grumphp.yml b/.github/workflows/grumphp.yml index 3b88047..bb91001 100644 --- a/.github/workflows/grumphp.yml +++ b/.github/workflows/grumphp.yml @@ -14,9 +14,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ['7.4', '8.1', '8.3'] + php-versions: ['7.4', '8.1', '8.2', '8.3'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 @@ -27,7 +27,7 @@ jobs: id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} # Use composer.json for key, if composer.lock is not committed. diff --git a/.gitignore b/.gitignore index 88b847b..32190a1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /nbproject/ .php_cs.cache .php-cs-fixer.cache +/generated \ No newline at end of file diff --git a/Console/Report.php b/Console/Report.php index 2341e9e..5f18463 100644 --- a/Console/Report.php +++ b/Console/Report.php @@ -25,9 +25,9 @@ class Report extends Command { public const ARGUMENT = 'period'; public const ARGUMENT_DESCRIPTION = 'Period. Possible values: ' - . self::TODAY_PERIOD . ', ' - . self::YESTERDAY_PERIOD . ', ' - . self::WEEK_PERIOD; + . self::TODAY_PERIOD . ', ' + . self::YESTERDAY_PERIOD . ', ' + . self::WEEK_PERIOD; public const COMMAND = 'mygento:kkm:report'; public const COMMAND_DESCRIPTION = 'Show report of kkm transaction for period.'; public const WEEK_PERIOD = 'week'; @@ -74,7 +74,7 @@ public function __construct( * @throws \Exception * @return int */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $this->output = $output; diff --git a/Console/SendRefund.php b/Console/SendRefund.php index d72b9e3..f0ee0aa 100644 --- a/Console/SendRefund.php +++ b/Console/SendRefund.php @@ -87,7 +87,7 @@ public function __construct( * @throws \Mygento\Kkm\Exception\VendorBadServerAnswerException * @return int|null */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $this->appState->setAreaCode(\Magento\Framework\App\Area::AREA_GLOBAL); diff --git a/Console/SendResell.php b/Console/SendResell.php index 8878e26..14b0860 100644 --- a/Console/SendResell.php +++ b/Console/SendResell.php @@ -80,7 +80,7 @@ public function __construct( * @throws \Mygento\Kkm\Exception\VendorBadServerAnswerException * @return int|null */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $this->appState->setAreaCode(\Magento\Framework\App\Area::AREA_GLOBAL); diff --git a/Console/SendSell.php b/Console/SendSell.php index 9ad5f82..7c88639 100644 --- a/Console/SendSell.php +++ b/Console/SendSell.php @@ -79,7 +79,7 @@ public function __construct( * @throws \Mygento\Kkm\Exception\VendorBadServerAnswerException * @return int|null */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $this->appState->setAreaCode(\Magento\Framework\App\Area::AREA_GLOBAL); diff --git a/Console/UpdateStatus.php b/Console/UpdateStatus.php index dc2909f..090a118 100644 --- a/Console/UpdateStatus.php +++ b/Console/UpdateStatus.php @@ -83,7 +83,7 @@ public function __construct( * @throws \Magento\Framework\Exception\LocalizedException * @return int|null */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $transactionUuid = $input->getArgument(self::TRANSACTION_UUID_ARGUMENT); @@ -138,7 +138,7 @@ protected function configure() To update all transaction with status 'wait': %command.full_name% HELP - . self::RUN_ALL_PARAM + . self::RUN_ALL_PARAM ); parent::configure(); } diff --git a/Model/Report.php b/Model/Report.php index 171a911..1ddb316 100644 --- a/Model/Report.php +++ b/Model/Report.php @@ -174,14 +174,14 @@ private function collectStatistics($searchCriteriaBuilder) ) ->where(sprintf('%s.%s = %s', $salesOrderAlias, OrderInterface::STORE_ID, $this->storeId)); } -// $transactionAttempts = $this->attemptRepository->getList( -// $searchCriteriaBuilder->create() -// ); + // $transactionAttempts = $this->attemptRepository->getList( + // $searchCriteriaBuilder->create() + // ); /** @var $statistics \Mygento\Kkm\Model\Statistics */ $statistics = $this->statisticsFactory->create(); -// $items = array_merge($transactions->getItems(), $transactionAttempts->getItems()); + // $items = array_merge($transactions->getItems(), $transactionAttempts->getItems()); $items = $transactions->getItems(); foreach ($items as $item) { $info = $item->getAdditionalInformation(TransactionEntity::RAW_DETAILS); diff --git a/Plugin/ExtraSalesViewToolbarButtons.php b/Plugin/ExtraSalesViewToolbarButtons.php index 6e63b0f..cf4bcfc 100644 --- a/Plugin/ExtraSalesViewToolbarButtons.php +++ b/Plugin/ExtraSalesViewToolbarButtons.php @@ -220,9 +220,9 @@ private function canBeShownCheckStatusButton($transactions, $storeId) foreach ($transactions as $transaction) { $status = $transaction->getKkmStatus(); // может быть завршенная транзакция по предоплате -// if ($status === Response::STATUS_DONE) { -// return false; -// } + // if ($status === Response::STATUS_DONE) { + // return false; + // } if ($status === Response::STATUS_WAIT) { $isWait = true; } diff --git a/README.md b/README.md index c733859..82b17b0 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,176 @@ -[![Build Status](https://travis-ci.com/mygento/kkm.svg?branch=v2.3)](https://travis-ci.com/mygento/kkm) +[![Build Status](https://github.com/mygento/kkm/actions/workflows/grumphp.yml/badge.svg)](https://github.com/mygento/kkm/actions/workflows/grumphp.yml) [![Latest Stable Version](https://poser.pugx.org/mygento/module-kkm/v/stable)](https://packagist.org/packages/mygento/module-kkm) [![Total Downloads](https://poser.pugx.org/mygento/module-kkm/downloads)](https://packagist.org/packages/mygento/module-kkm) # Модуль интеграции Онлайн касс для Magento 1/2 Модуль разрабатывается для полной поддержки требований 54 ФЗ интернет-магазинами на Magento 1 и 2 для сервисов: -* АТОЛ онлайн. Модуль поддерживает версию сервиса АТОЛ v4 (ФФД 1.05). -* Чеконлайн. + +- АТОЛ онлайн. Модуль поддерживает версию сервиса АТОЛ v4 (ФФД 1.05). +- Чеконлайн. ## Функциональность модуля ### Передача данных в Онлайн кассу -* отправляет данные о счете/возврате: - * автоматически при создании счета (настраивается в конфигурации) - * автоматически при создании возврата (настраивается в конфигурации) - * вручную одной из консольных команд (см. ниже) - * вручную из админки кнопкой на странице Счета или Возврата + +- отправляет данные о счете/возврате: + - автоматически при создании счета (настраивается в конфигурации) + - автоматически при создании возврата (настраивается в конфигурации) + - вручную одной из консольных команд (см. ниже) + - вручную из админки кнопкой на странице Счета или Возврата ### Повторная передача данных в Онлайн кассу (Resell) + (Не путать с чеком коррекции) -* Отменяет предыдущий чек прихода (по Invoice) и отправляет новый. - * вручную при нажатии кнопки `Resell` в админке - * консольной командой (см. ниже) - * другой модуль может триггерить `\Mygento\Kkm\Api\Processor\SendInterface::proceedResellRefund` - + +- Отменяет предыдущий чек прихода (по Invoice) и отправляет новый. + - вручную при нажатии кнопки `Resell` в админке + - консольной командой (см. ниже) + - другой модуль может триггерить `\Mygento\Kkm\Api\Processor\SendInterface::proceedResellRefund` + ### Получение данных из АТОЛ -* получает из АТОЛ данные о статусе регистрации счета/возврата - * автоматически (настраивается в конфигурации). После обработки данных АТОЛ отправляет результат обратно (колбек). По умолчанию URL: http://shop.ru/kkm/frontend/callback - * крон задачей для проверки статусов - * вручную из админки кнопкой на странице Счета или Возврата - * консольной командой `mygento:kkm:update-one {$uuid}` или `mygento:kkm:update-all {$storeId}` + +- получает из АТОЛ данные о статусе регистрации счета/возврата + - автоматически (настраивается в конфигурации). После обработки данных АТОЛ отправляет результат обратно (колбек). По умолчанию URL: http://shop.ru/kkm/frontend/callback + - крон задачей для проверки статусов + - вручную из админки кнопкой на странице Счета или Возврата + - консольной командой `mygento:kkm:update-one {$uuid}` или `mygento:kkm:update-all {$storeId}` ### Получение данных из Чеконлайн + Работа сервиса Чеконлайн построена по синхронному принципу, понятие «статус» документа в сервисе отсутствует. Вместо этого используются кэширование ответов. Ключём кэша являются поля Group, RequestId, ClientId, что значит, что если будут отправлены запросы с одинаковыми указанными полями, то сервис ответит данными из кэша. В кэш помещаются успешные ответы и некоторые ошибки устройства Ккм (см. документацию Чеконлайн) ### Процесс отправки данных в Онлайн кассу + 1. На основании сущности Invoice или Creditmemo формируется объект `Mygento\Kkm\Api\Data\RequestInterface`. - 1.1. При асинхронной передаче - объект помещается в очередь (см. Magento Queue Framework) - 1.2. При синхронной передаче - передается классу `Vendor` для отправки + 1.1. При асинхронной передаче - объект помещается в очередь (см. Magento Queue Framework) + 1.2. При синхронной передаче - передается классу `Vendor` для отправки 2. Регистрируется попытка отправки данных. Создается сущность `Api\Data\TransactionInterface\TransactionAttemptInterface` со статусом `NEW` (1) 3. Осуществляется передача данных в виде JSON. - - 3.1. В случае **УСПЕШНОЙ** передачи (один из HTTP статусов `[200, 400, 401]`) - * создается транзакция - сущность `Magento\Sales\Api\Data\TransactionInterface` в который записываются уникальный идентификатор запроса (UUID - Атол; RequestId - Чеконлайн) и все данные о передаче. В админке это грид Sales -> Transactions. - * Сущность попытки отправки `TransactionAttemptInterface` получает статус `Sent` (2) - * Создается комментарий к заказу - * Транзакция получает в ККМ-статус (kkm_status): - * Атол - `wait` - * Чеконлайн - `done` - - 3.2. В случае **НЕУСПЕШНОЙ** передачи (статусы отличные от `[200, 400, 401]` (так же `500` для Чеконлайн), отсутствие ответа от сервера, некорректные данные в инвойсе или возврате) - * Сущность попытки отправки `TransactionAttemptInterface` получает статус `Error` (3) - * Создается комментарий к заказу с описанием причины ошибки - * Заказ получает статус "KKM Failed" - * Если выброшено исключение `VendorBadServerAnswerException` (сервер не отвечает и еще в некоторых случаях) и включена асинхронная передача - то отправка будет снова помещена в очередь. - * Если выброшено исключение `VendorNonFatalErrorException` и включена асинхронная передача - то: - * Атол - выполняется генерация нового external_id и отправка будет снова помещена в очередь. - * Чеконлайн - Сущность транзакции получает статус `wait` и отправка снова помещается в очередь без генерации нового external_id, т.к. ответ с нефатальной ошибкой не кэшируется. Так же при работе «облачного» сервиса Чеконлайн могут возникать ошибки - возвращающие HTTP код 500 и структуру, содержащая поля: `FCEError`, `ErrorDescription`, `Device` и `Fatal`. Поле `Fatal` со значением `true` показывает, что повторное выполнение запроса приведёт к - ошибке. Если поле `Fatal` равно `false` то отправка так же помещается в очередь. + + 3.1. В случае **УСПЕШНОЙ** передачи (один из HTTP статусов `[200, 400, 401]`) + + - создается транзакция - сущность `Magento\Sales\Api\Data\TransactionInterface` в который записываются уникальный идентификатор запроса (UUID - Атол; RequestId - Чеконлайн) и все данные о передаче. В админке это грид Sales -> Transactions. + - Сущность попытки отправки `TransactionAttemptInterface` получает статус `Sent` (2) + - Создается комментарий к заказу + - Транзакция получает в ККМ-статус (kkm_status): + + - Атол - `wait` + - Чеконлайн - `done` + + 3.2. В случае **НЕУСПЕШНОЙ** передачи (статусы отличные от `[200, 400, 401]` (так же `500` для Чеконлайн), отсутствие ответа от сервера, некорректные данные в инвойсе или возврате) + + - Сущность попытки отправки `TransactionAttemptInterface` получает статус `Error` (3) + - Создается комментарий к заказу с описанием причины ошибки + - Заказ получает статус "KKM Failed" + - Если выброшено исключение `VendorBadServerAnswerException` (сервер не отвечает и еще в некоторых случаях) и включена асинхронная передача - то отправка будет снова помещена в очередь. + - Если выброшено исключение `VendorNonFatalErrorException` и включена асинхронная передача - то: + - Атол - выполняется генерация нового external_id и отправка будет снова помещена в очередь. + - Чеконлайн - Сущность транзакции получает статус `wait` и отправка снова помещается в очередь без генерации нового external_id, т.к. ответ с нефатальной ошибкой не кэшируется. Так же при работе «облачного» сервиса Чеконлайн могут возникать ошибки + возвращающие HTTP код 500 и структуру, содержащая поля: `FCEError`, `ErrorDescription`, `Device` и `Fatal`. Поле `Fatal` со значением `true` показывает, что повторное выполнение запроса приведёт к + ошибке. Если поле `Fatal` равно `false` то отправка так же помещается в очередь. 4. Только Атол. Модуль автоматически запрашивает у АТОЛа статус по всем транзакциям с ККМ-статусом `wait` - - 4.1 Попытки обновления статуса прекращаются, когда транзакция получает статус `done` - - 4.2 Максимальное количество попыток настройкой модуля ККМ. - -5. В случае **НЕУСПЕШНОЙ** передачи выполняется несколько попыток отправки с увеличивающимися интервалами (например через 1 минуту, 5 минут, 15 минут, 30 минут, 1 час). - - 5.1 Настройка интервалов доступна в настройках модуля ККМ. - - 5.2 Максимальное количество попыток отправки тажке ограничего настройкой модуля ККМ. - - 5.3 В случае, когда достигается максимальное количество попыток отправки, счетчик попыток обнуляется и отправка возобновляется через сутки. + + 4.1 Попытки обновления статуса прекращаются, когда транзакция получает статус `done` + + 4.2 Максимальное количество попыток настройкой модуля ККМ. + +5. В случае **НЕУСПЕШНОЙ** передачи выполняется несколько попыток отправки с увеличивающимися интервалами (например через 1 минуту, 5 минут, 15 минут, 30 минут, 1 час). + + 5.1 Настройка интервалов доступна в настройках модуля ККМ. + + 5.2 Максимальное количество попыток отправки тажке ограничего настройкой модуля ККМ. + + 5.3 В случае, когда достигается максимальное количество попыток отправки, счетчик попыток обнуляется и отправка возобновляется через сутки. ### Процесс повторной отправки данных (Resell) + Работает только для тех чеков, которые были отправлены и имеют статус `Done`. 1. На основании Invoice создается чек возврата (refund) и отправляется в Онлайн кассу. 2. Создается новая запись Payment Transaction, дочерняя от предыдущей отправки `sell` по этому инвойсу. 3. Когда статус отправки из п.1 становится `Done` (Для Чеконлайн статус отправки сразу становится `Done` в случае успеха) - формируется и отправляется новый чек прихода (sell). 4. Для нового чека прихода создается новая запись Payment Transaction, дочерняя от транзакции для чека возврата (п.2). - 5. Resell считается завершенным, если новый чек прихода (п.3) получает статус `Done`. Обновление статуса происходит так же как и во всех остальных случаях (Для Чеконлайн обновление статуса не происходит т.к. работа сервиса устроена по синхронному принципу) - +5. Resell считается завершенным, если новый чек прихода (п.3) получает статус `Done`. Обновление статуса происходит так же как и во всех остальных случаях (Для Чеконлайн обновление статуса не происходит т.к. работа сервиса устроена по синхронному принципу) ### Отчеты -Модуль отправляет отчеты об отправленных данных в Онлайн кассу на емейл (в конфиге). Неуспешные отправки отображаются в этом же письме с доп.деталями. Также этот отчет можно посмотреть в консоли. -* Еженедельный (за прошлую неделю), Ежедневный (за текущий день), Ежедневный (за вчерашний день) -* Верстка письма. Файл `view/adminhtml/templates/email/kkm_report-mjml.mjml` содержит верстку письма. Редактируется с помощью сервиса https://mjml.io/ +Модуль отправляет отчеты об отправленных данных в Онлайн кассу на емейл (в конфиге). Неуспешные отправки отображаются в этом же письме с доп.деталями. Также этот отчет можно посмотреть в консоли. +- Еженедельный (за прошлую неделю), Ежедневный (за текущий день), Ежедневный (за вчерашний день) +- Верстка письма. Файл `view/adminhtml/templates/email/kkm_report-mjml.mjml` содержит верстку письма. Редактируется с помощью сервиса https://mjml.io/ ### Поддержка новых версий сервиса АТОЛ Онлайн + Модуль поддерживал версии сервиса v3 и v4. Если выйдет новая версия, необходимо сделать след.шаги: + 1. создать class RequestForVersionX наследник абстрактного класса Request -2. релилизовать его JSON представление - метод jsonSerialize() -3. добавить создание объекта реквеста в Mygento\Kkm\Model\Atol\RequestFactory +2. релилизовать его JSON представление - метод jsonSerialize() +3. добавить создание объекта реквеста в Mygento\Kkm\Model\Atol\RequestFactory 4. добавить инфу о новой версии сервиса в сурс модель Mygento\Kkm\Model\Source\ApiVersion ### Использование очередей -* отправка сообщений в Онлайн кассу может осуществляться в двух режимах: - * синхронный (сразу после сохранения сущности или ручной отправки); - * асинхронно (через нативный механизм очередей сообщений Magento). -* режим работы настраивается в конфигурации + +- отправка сообщений в Онлайн кассу может осуществляться в двух режимах: + - синхронный (сразу после сохранения сущности или ручной отправки); + - асинхронно (через нативный механизм очередей сообщений Magento). +- режим работы настраивается в конфигурации ### Ручная отправка данных -* Отправка данных на странице сущности -* Отправка данных консольной командой с указанием IncrementId сущности + +- Отправка данных на странице сущности +- Отправка данных консольной командой с указанием IncrementId сущности ### Логирование сообщений -* Модуль логирует (при включенном режиме Debug в Stores -> Configuration -> Mygento Extensions -> Extensions and Support) все запросы (и ответы). -* Лог запросов доступен на странице конфигурации модуля + +- Модуль логирует (при включенном режиме Debug в Stores -> Configuration -> Mygento Extensions -> Extensions and Support) все запросы (и ответы). +- Лог запросов доступен на странице конфигурации модуля ## Список Rewrite + нет ## Список событий и плагинов, Описание действий и причины ### События -* **sales_order_invoice_save_commit_after**: - * отправляет данные по инвойсу после его сохранения. -* **sales_order_creditmemo_save_commit_after**: - * отправляет данные по возврату после сохранения. + +- **sales_order_invoice_save_commit_after**: + - отправляет данные по инвойсу после его сохранения. +- **sales_order_creditmemo_save_commit_after**: + - отправляет данные по возврату после сохранения. ### Плагины -* before плагин `ExtraSalesViewToolbarButtons` на метод `Magento\Backend\Block\Widget\Button\Toolbar::pushButtons` добавляет кнопки Отправки в Онлайн кассу и кнопку проверки статуса на страницу сущности в админке + +- before плагин `ExtraSalesViewToolbarButtons` на метод `Magento\Backend\Block\Widget\Button\Toolbar::pushButtons` добавляет кнопки Отправки в Онлайн кассу и кнопку проверки статуса на страницу сущности в админке ## Список доступных реализованных API + нет ## Список встроенных тестов, что и как они тестируют + нет ## Cron-процессы -* **kkm_statuses** - * Только Атол. Обновление статуса: job обновляет статусы транзакций, у которых статус `wait`. По умолчанию каждую минуту -* **kkm_proceed_scheduled_attempt** - * выполняет повторные попытки отправки запросов по заданному расписанию (scheduled_at). -* **kkm_report** - * Отчет: job отправки отчета. Частота конфигурируется в админке на стр. настроек модуля. По умолчанию ежедневно в 00:07 + +- **kkm_statuses** + - Только Атол. Обновление статуса: job обновляет статусы транзакций, у которых статус `wait`. По умолчанию каждую минуту +- **kkm_proceed_scheduled_attempt** + - выполняет повторные попытки отправки запросов по заданному расписанию (scheduled_at). +- **kkm_report** + - Отчет: job отправки отчета. Частота конфигурируется в админке на стр. настроек модуля. По умолчанию ежедневно в 00:07 ## Консольные команды -* `mygento:kkm:report` - Отображает отчет. Аргументы: today, yesterday, week -* `mygento:kkm:refund` - Отправляет возврат. Аргументы: IncrementId сущности -* `mygento:kkm:sell` - Отправляет счет. Аргументы: IncrementId сущности -* `mygento:kkm:resell` - Запускает процесс resell. Отправляет refund по текущему чеку. Аргументы: IncrementId сущности. При указании ключа `-f` увеличится external_id. -* `mygento:kkm:update-all` - Только Атол. Запрашивает данные о статусе всех отправок со статусом `wait` для указанного стора. Аргументы: StoreID -* `mygento:kkm:update-one` - Только Атол. Запрашивает данные о статусе указанной отправки. Аргументы: UUID + +- `mygento:kkm:report` - Отображает отчет. Аргументы: today, yesterday, week +- `mygento:kkm:refund` - Отправляет возврат. Аргументы: IncrementId сущности +- `mygento:kkm:sell` - Отправляет счет. Аргументы: IncrementId сущности +- `mygento:kkm:resell` - Запускает процесс resell. Отправляет refund по текущему чеку. Аргументы: IncrementId сущности. При указании ключа `-f` увеличится external_id. +- `mygento:kkm:update-all` - Только Атол. Запрашивает данные о статусе всех отправок со статусом `wait` для указанного стора. Аргументы: StoreID +- `mygento:kkm:update-one` - Только Атол. Запрашивает данные о статусе указанной отправки. Аргументы: UUID diff --git a/composer.json b/composer.json index 007bc85..7f62960 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,6 @@ { "name": "mygento/module-kkm", "type": "magento2-module", - "version": "2.4.1", "license": "OSL-3.0", "description": "Модуль интеграции фискальных кассовых аппаратов для Magento в соответствии с 54-ФЗ", "keywords": [