Інтеграція РРО в Python
Головна ідея: розробити Python-сервіс або Python-адаптер для інтеграції ERP / POS / CRM / інтернет-магазину з фізичним фіскальним реєстратором МІНІ-ФП54.01 для друку та фіскалізації чеків, повернень, службових операцій, відкриття і закриття змін.
Критично важливо: це інтеграція з фізичним РРО, а не з хмарним ПРРО. Python-сервіс повинен працювати через драйвер, COM/OLE/DLL, протокол обміну, USB/RS232 або проміжний локальний агент.
Важливо: для МІНІ-ФП54.01 виробник надає USB-драйвер, OLE/DLL-бібліотеку для фіскальних реєстраторів, інструкції та документи щодо протоколу обміну. Перед розробкою потрібно завантажити та перевірити актуальні версії цих компонентів.
Рекомендована архітектура: Python POS Adapter працює локально на комп'ютері касира або POS-вузлі, має доступ до USB/RS232/COM/OLE/DLL-драйвера та приймає команди від ERP через HTTP API або локальну чергу.
Управлінський результат: керівник повинен бачити, скільки чеків надруковано, скільки повернень виконано, які зміни відкриті, які Z-звіти сформовані, які РРО мають помилки зв'язку або потребують уваги.
1. Мета
Метою задачі є створення Python-рішення для інтеграції з фізичним фіскальним реєстратором МІНІ-ФП54.01.
Рішення повинно забезпечити:
- підключення до РРО через USB, RS232 або драйвер;
- роботу через OLE/DLL-бібліотеку виробника або прямий протокол обміну;
- перевірку стану РРО;
- відкриття касової зміни;
- друк і фіскалізацію чека продажу;
- друк і фіскалізацію чека повернення;
- службове внесення готівки;
- службове винесення готівки;
- формування X-звіту;
- формування Z-звіту;
- друк нефіскального тексту, якщо підтримується;
- програмування товарів, податкових груп, касирів — якщо потрібно;
- контроль помилок РРО;
- журналювання команд і відповідей;
- захист від дублювання чеків;
- повторну обробку технічних помилок;
- інтеграцію з K2 ERP / POS / CRM / сайтом.
2. Область застосування
Інтеграція призначена для:
- фізичних магазинів;
- аптек;
- кафе, барів, ресторанів;
- кіосків;
- торгових точок із обмеженим простором;
- виїзної торгівлі;
- кур'єрської доставки;
- інтернет-магазинів із друком фіскального чека на фізичному РРО;
- POS-вузлів, де потрібна робота з реальним фіскальним реєстратором.
3. Особливості моделі МІНІ-ФП54.01
| Параметр | Значення / опис |
|---|---|
| Тип пристрою | Фізичний фіскальний реєстратор. |
| Контрольна стрічка | КСЕФ / КЛЕФ. |
| Передача даних у ДПС | Через Ethernet або GSM/GPRS-модем. |
| Підключення до ПК | USB, RS232. |
| Bluetooth | Опція. |
| Кількість товарів | 16 384. |
| Кількість відділів | 64. |
| Кількість касирів | 32. |
| Ширина чекової стрічки | 58 мм. |
| Швидкість друку | 8 рядків/с. |
| Дисплей покупця | Вбудований 2x16. |
| Акумулятор | Вбудована Li-Pol батарея. |
| Друк QR / штрих-коду | Підтримується. |
| Грошова скринька | Порт керування micro-jack 2,5 мм. |
Критично важливо: Ethernet і GSM/GPRS у цій моделі використовуються для передачі даних РРО до ДПС, але інтеграція з Python для команд друку чеків зазвичай потребує USB/RS232 або драйвера/OLE/DLL на локальному комп'ютері.
4. Варіанти інтеграції Python з РРО
4.1. Варіант 1. Через OLE/DLL-бібліотеку виробника
Python взаємодіє з OLE/COM або DLL-бібліотекою, яку надає виробник.
| Параметр | Опис |
|---|---|
| Підходить для | Windows POS, касових робочих місць, локальної інтеграції. |
| Переваги | Використання офіційної бібліотеки виробника. |
| Обмеження | Найчастіше потребує Windows, COM/OLE, драйверів і локального доступу до пристрою. |
| Python-підхід | pywin32 / comtypes / ctypes залежно від типу бібліотеки. |
Рекомендовано для MVP: починати з OLE/DLL-бібліотеки виробника, якщо вона стабільно працює з моделлю МІНІ-ФП54.01 та підтримує всі потрібні команди.
4.2. Варіант 2. Через прямий протокол обміну RS232/USB-COM
Python відкриває COM-порт і відправляє команди згідно з протоколом обміну.
| Параметр | Опис |
|---|---|
| Підходить для | Глибокої інтеграції, Linux/Windows-сценаріїв, embedded POS. |
| Переваги | Менше залежності від COM/OLE, потенційно кросплатформено. |
| Обмеження | Потрібно точно реалізувати протокол, контрольні суми, таймаути, кодування, стани помилок. |
| Python-підхід | pyserial. |
Важливо: прямий протокол потрібно реалізовувати тільки після отримання актуальної документації виробника щодо команд, форматів пакетів, контрольних сум і відповідей РРО.
4.3. Варіант 3. Локальний Python Agent + ERP API
На касовому ПК запускається локальний Python Agent, який працює з РРО. ERP надсилає йому HTTP-запити.
| Компонент | Опис |
|---|---|
| K2 ERP / POS | Створює продаж або повернення. |
| Python Agent | Локальний сервіс на касовому ПК. |
| РРО МІНІ-ФП54.01 | Фізичний пристрій, підключений через USB/RS232. |
| Dashboard | Центральний контроль чеків, змін і помилок. |
Рекомендована схема для K2 ERP: K2 ERP не повинна напряму керувати COM-портом. Краще використовувати локальний Python Agent біля РРО, а K2 ERP працює з ним через API.
5. Загальна архітектура
K2 ERP / POS / CRM / Website
|
| 1. Продаж / повернення / службова операція
v
Central Fiscal API
|
| 2. Черга, журнал, дедублікація
v
Local Python RRO Agent
|
| 3. OLE/DLL або Serial Protocol
v
МІНІ-ФП54.01
|
| 4. Друк та фіскалізація чека
v
Покупець / чекова стрічка
|
| 5. Передача даних до ДПС самим РРО
v
ДПС
6. Основні сутності
| Сутність | Опис |
|---|---|
| RRO Device | Фізичний фіскальний реєстратор МІНІ-ФП54.01. |
| RRO Agent | Локальний Python-сервіс, який керує пристроєм. |
| Cashier | Касир, від імені якого виконується операція. |
| Shift | Касова зміна. |
| Fiscal Receipt | Фіскальний чек продажу. |
| Refund Receipt | Чек повернення. |
| Service Operation | Службове внесення або винесення. |
| X Report | Проміжний звіт без закриття зміни. |
| Z Report | Звіт із закриттям зміни. |
| RRO Command | Команда, що відправляється до фіскального реєстратора. |
| RRO Response | Відповідь пристрою. |
| RRO Error | Помилка пристрою, драйвера або з'єднання. |
7. User Story
7.1. Продаж
Як POS або K2 ERP, я хочу передати продаж у Python Agent, щоб агент надрукував і фіскалізував чек на МІНІ-ФП54.01.
7.2. Повернення
Як касир, я хочу створити чек повернення, щоб коректно повернути кошти покупцю та відобразити операцію в РРО.
7.3. Відкриття зміни
Як касир, я хочу відкрити зміну на РРО, щоб мати можливість друкувати фіскальні чеки.
7.4. Закриття зміни
Як касир або адміністратор, я хочу сформувати Z-звіт, щоб закрити касову зміну.
7.5. Контроль помилок
Як керівник або адміністратор, я хочу бачити помилки РРО, щоб швидко реагувати на проблеми з папером, зв'язком, портом, драйвером або фіскалізацією.
8. Функціональні вимоги
8.1. Налаштування пристрою
У K2 ERP або локальному агенті повинна бути картка РРО.
| Поле | Тип | Обов'язковість | Опис |
|---|---|---|---|
| device_name | string | Так | Назва РРО. |
| device_model | string | Так | МІНІ-ФП54.01. |
| device_serial_number | string | Так | Серійний номер пристрою. |
| fiscal_number | string | Так | Фіскальний номер РРО. |
| connection_type | enum | Так | OLE_DLL, SERIAL, USB_COM. |
| com_port | string | Ні | Наприклад COM3. |
| baud_rate | integer | Ні | Швидкість порту, якщо використовується serial. |
| dll_path | string | Ні | Шлях до DLL, якщо використовується DLL. |
| ole_progid | string | Ні | ProgID OLE-сервера, якщо використовується OLE. |
| cashier_id | string | Ні | Касир за замовчуванням. |
| tax_profile_id | string | Ні | Профіль податкових ставок. |
| auto_open_shift | boolean | Так | Автоматично відкривати зміну перед першим чеком. |
| allow_service_operations | boolean | Так | Дозволити службове внесення/винесення. |
| is_active | boolean | Так | Чи активний пристрій. |
8.2. Перевірка стану РРО
Python Agent повинен уміти перевіряти:
- чи підключений РРО;
- чи доступний порт;
- чи доступний драйвер/OLE/DLL;
- чи є папір;
- чи відкрита кришка;
- чи є помилки живлення;
- чи є зв'язок з ДПС через канали пристрою;
- чи відкрита зміна;
- чи не заблокований РРО;
- чи не переповнена пам'ять;
- чи коректно встановлена дата і час;
- чи готовий РРО до друку чека.
Критично важливо: перед друком фіскального чека агент повинен перевірити готовність РРО. Якщо РРО має критичну помилку, чек не повинен переходити в статус «Фіскалізовано».
8.3. Відкриття зміни
Локальний endpoint:
POST /api/v1/rro/shifts/open
Логіка:
1. Перевірити підключення до РРО. 2. Перевірити стан касира. 3. Перевірити, чи зміна вже відкрита. 4. Якщо зміна не відкрита — виконати команду відкриття зміни. 5. Зберегти локальний статус. 6. Повернути результат у K2 ERP / POS.
8.4. Чек продажу
Локальний endpoint:
POST /api/v1/rro/receipts/sale
Мінімальні дані:
| Поле | Тип | Опис |
|---|---|---|
| external_order_id | string | ID замовлення у K2 ERP / POS. |
| idempotency_key | string | Ключ захисту від дублювання. |
| cashier_id | string | Касир. |
| items | array | Позиції чека. |
| payments | array | Оплати. |
| total_amount | decimal | Загальна сума. |
| customer | object | Дані покупця, якщо потрібні. |
| print_qr | boolean | Чи друкувати QR-код. |
8.5. Приклад запиту на чек продажу
{
"external_order_id": "ORDER-2026-000123",
"idempotency_key": "ORDER-2026-000123-PAY-123456",
"cashier_id": "cashier-001",
"items": [
{
"name": "Товар 1",
"sku": "SKU-001",
"quantity": 2,
"price": 250.00,
"amount": 500.00,
"tax_group": "VAT_20",
"department": 1,
"unit": "шт"
},
{
"name": "Доставка",
"sku": "DELIVERY",
"quantity": 1,
"price": 70.00,
"amount": 70.00,
"tax_group": "NO_VAT",
"department": 2,
"unit": "послуга"
}
],
"payments": [
{
"type": "CARD",
"amount": 570.00,
"provider": "terminal",
"payment_id": "PAY-123456"
}
],
"total_amount": 570.00,
"print_qr": true
}
8.6. Чек повернення
Endpoint:
POST /api/v1/rro/receipts/refund
Мінімальні дані:
| Поле | Тип | Опис |
|---|---|---|
| original_receipt_id | uuid | Внутрішній ID первинного чека. |
| original_fiscal_number | string | Фіскальний номер первинного чека, якщо доступний. |
| external_refund_id | string | ID повернення в ERP / POS. |
| items | array | Позиції, які повертаються. |
| payments | array | Сума повернення. |
| reason | string | Причина повернення. |
| idempotency_key | string | Ключ захисту від дублювання. |
Критично важливо: чек повернення не повинен перевищувати залишок по первинному чеку. Для часткових повернень система повинна вести залишок доступної до повернення суми та кількості.
8.7. Службове внесення / винесення
Endpoint:
POST /api/v1/rro/service-operation
Приклад:
{
"operation_type": "CASH_IN",
"amount": 1000.00,
"cashier_id": "cashier-001",
"comment": "Службове внесення на початок зміни",
"idempotency_key": "CASH-IN-2026-05-07-001"
}
Типи:
| Тип | Опис |
|---|---|
| CASH_IN | Службове внесення готівки. |
| CASH_OUT | Службове винесення готівки. |
8.8. X-звіт
Endpoint:
POST /api/v1/rro/reports/x
Призначення:
- отримати проміжний звіт без закриття зміни;
- перевірити обороти;
- перевірити стан каси;
- показати керівнику поточні підсумки.
8.9. Z-звіт
Endpoint:
POST /api/v1/rro/reports/z
Логіка:
1. Перевірити підключення до РРО. 2. Перевірити відкриту зміну. 3. Перевірити незавершені чеки. 4. Виконати команду формування Z-звіту. 5. Зберегти номер і результат Z-звіту. 6. Закрити локальну зміну. 7. Передати результат у K2 ERP.
Критично важливо: Z-звіт є операцією закриття зміни. Система повинна обмежувати доступ до цієї дії та логувати, хто її виконав.
9. Статуси чеків
| Статус | Код | Опис | Колір |
|---|---|---|---|
| Чернетка | DRAFT | Чек створено в Python-сервісі, але не відправлено на РРО. | Сірий |
| Очікує друку | PENDING | Чек у черзі на друк. | Жовтий |
| Відправляється на РРО | SENDING_TO_RRO | Команда передається в драйвер або COM-порт. | Блакитний |
| Друкується | PRINTING | РРО виконує друк. | Блакитний |
| Фіскалізовано | FISCALIZED | Чек успішно надруковано і зареєстровано РРО. | Зелений |
| Помилка РРО | RRO_ERROR | РРО повернув помилку. | Червоний |
| Помилка з'єднання | CONNECTION_ERROR | Немає зв'язку з РРО або драйвером. | Червоний |
| Потребує повтору | NEEDS_RETRY | Операцію можна повторити. | Помаранчевий |
| Скасовано | CANCELLED | Операцію скасовано до друку. | Сірий |
| Повернення | REFUNDED | По чеку створено повне або часткове повернення. | Фіолетовий |
10. Статуси РРО
| Статус | Код | Опис | Колір |
|---|---|---|---|
| Готовий | READY | РРО готовий до роботи. | Зелений |
| Не підключений | DISCONNECTED | Немає зв'язку з пристроєм. | Червоний |
| Немає паперу | PAPER_OUT | Потрібно замінити рулон. | Помаранчевий |
| Відкрита кришка | COVER_OPEN | Кришка принтера відкрита. | Помаранчевий |
| Помилка фіскальної пам'яті | FISCAL_MEMORY_ERROR | Критична помилка. | Червоний |
| Немає зв'язку з ДПС | TAX_SERVER_CONNECTION_ERROR | Пристрій не може передати дані. | Помаранчевий |
| Зміна відкрита | SHIFT_OPEN | Можна друкувати фіскальні чеки. | Зелений |
| Зміна закрита | SHIFT_CLOSED | Перед продажем потрібно відкрити зміну. | Сірий |
| Заблоковано | BLOCKED | Робота неможлива. | Червоний |
11. Єдина логіка кольорів
| Колір | HTML | Значення | Де використовується |
|---|---|---|---|
| Зелений | #c8e6c9 | Успішна операція або нормальний стан. | Dashboard, список чеків, статус РРО. |
| Блакитний | #bbdefb | Операція виконується. | Черга, друк, передача команди. |
| Жовтий | #fff9c4 | Очікування або попередження. | Черга чеків, очікування друку. |
| Помаранчевий | #ffcc80 | Потрібна дія користувача. | Немає паперу, кришка, повтор. |
| Червоний | #ef9a9a | Критична помилка. | Помилка РРО, порт недоступний, фіскальна помилка. |
| Фіолетовий | #f3e5f5 | Повернення або спеціальна операція. | Refund, службові операції. |
| Сірий | #eeeeee | Неактивно або скасовано. | Чернетка, зміна закрита. |
12. Python RRO Agent
12.1. Призначення
Python RRO Agent — це локальний сервіс, який встановлюється на касовий ПК і має доступ до РРО через USB/RS232/OLE/DLL.
Він повинен:
- приймати HTTP-запити від K2 ERP / POS;
- керувати РРО;
- виконувати друк чеків;
- повертати статуси;
- зберігати локальний журнал;
- працювати навіть при тимчасовій недоступності центральної системи, якщо це дозволено сценарієм;
- синхронізувати результати з центральною БД.
12.2. Рекомендований стек агента
| Компонент | Технологія |
|---|---|
| Мова | Python 3.11+ |
| API | FastAPI |
| Доступ до COM/OLE | pywin32 або comtypes |
| Доступ до DLL | ctypes або cffi |
| Доступ до COM-порту | pyserial |
| Локальна БД | SQLite або PostgreSQL |
| Черга | SQLite queue / Redis / RQ |
| Логи | structlog / logging |
| Упаковка | Windows Service / Docker для Linux-сценаріїв, якщо serial |
12.3. Методи Python RRO Client
class MiniFP54Client:
def check_connection(self) -> "RROStatus":
pass
def get_status(self) -> "RROStatus":
pass
def open_shift(self, cashier_id: str) -> "ShiftResponse":
pass
def close_shift(self) -> "ZReportResponse":
pass
def print_x_report(self) -> "XReportResponse":
pass
def print_sale_receipt(self, payload: "SaleReceiptPayload") -> "ReceiptResponse":
pass
def print_refund_receipt(self, payload: "RefundReceiptPayload") -> "ReceiptResponse":
pass
def service_cash_in(self, amount: float, comment: str | None = None) -> "ServiceOperationResponse":
pass
def service_cash_out(self, amount: float, comment: str | None = None) -> "ServiceOperationResponse":
pass
def print_non_fiscal_text(self, lines: list[str]) -> "PrintResponse":
pass
13. Приклад конфігурації
from pydantic_settings import BaseSettings
class MiniFP54Settings(BaseSettings):
connection_type: str = "OLE_DLL"
com_port: str | None = "COM3"
baud_rate: int = 115200
dll_path: str | None = None
ole_progid: str | None = None
timeout_seconds: int = 30
retry_count: int = 2
retry_backoff_seconds: int = 3
auto_open_shift: bool = True
log_raw_commands: bool = True
allow_refunds: bool = True
allow_service_operations: bool = True
Приклад `.env`:
MINI_FP54_CONNECTION_TYPE=OLE_DLL MINI_FP54_COM_PORT=COM3 MINI_FP54_BAUD_RATE=115200 MINI_FP54_TIMEOUT_SECONDS=30 MINI_FP54_RETRY_COUNT=2 MINI_FP54_AUTO_OPEN_SHIFT=true MINI_FP54_LOG_RAW_COMMANDS=true
14. Валідація чека
Перед відправкою на РРО система повинна перевірити:
- наявність external_order_id;
- наявність idempotency_key;
- відсутність уже фіскалізованого чека з таким ключем;
- наявність відкритої зміни або можливість її відкрити;
- готовність РРО;
- наявність паперу;
- відсутність критичних помилок;
- наявність хоча б однієї позиції;
- коректність кількості;
- коректність ціни;
- коректність суми рядка;
- відповідність total_amount сумі товарів і оплат;
- коректність типу оплати;
- коректність податкових груп;
- довжину назви товару;
- довжину рядка чека;
- наявність відділу, якщо він обов'язковий;
- коректність QR-коду, якщо він друкується.
Важливо: МІНІ-ФП54.01 має обмеження на кількість символів у рядку та в назві товару. Python Agent повинен або обрізати рядки за правилом, або повертати помилку валідації.
15. Дедублікація
Система повинна не допускати дублювання чеків.
Ключі дедублікації:
| Ключ | Призначення |
|---|---|
| external_order_id | ID замовлення у зовнішній системі. |
| external_payment_id | ID оплати. |
| idempotency_key | Основний ключ повторного запиту. |
| receipt_hash | Hash товарів, сум, оплат і каси. |
Приклад:
sha256(external_order_id + total_amount + payment_id + device_serial_number)
Критично важливо: повторний запит із тим самим idempotency_key не повинен друкувати другий фіскальний чек. Він повинен повернути результат уже виконаної операції.
16. Черга друку
16.1. Логіка черги
1. POS / K2 ERP надсилає запит на чек. 2. Python Agent виконує валідацію. 3. Створюється локальний запис receipt зі статусом PENDING. 4. Чек потрапляє в чергу друку. 5. Worker перевіряє стан РРО. 6. Якщо потрібно — відкриває зміну. 7. Worker друкує чек. 8. Результат зберігається локально. 9. Результат синхронізується з центральною системою.
16.2. Пріоритети
| Тип задачі | Пріоритет | Коментар |
|---|---|---|
| Чек продажу | Високий | Основна операція. |
| Чек повернення | Високий | Фінансова операція. |
| Z-звіт | Критичний | Закриття зміни. |
| X-звіт | Середній | Контрольний звіт. |
| Службове внесення / винесення | Середній | Касова операція. |
| Нефіскальний друк | Низький | Не блокує продажі. |
17. Модель даних
17.1. rro_devices
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID пристрою. |
| model | varchar | MINI_FP54_01. |
| serial_number | varchar | Серійний номер. |
| fiscal_number | varchar | Фіскальний номер. |
| connection_type | varchar | OLE_DLL, SERIAL, USB_COM. |
| com_port | varchar | COM-порт. |
| baud_rate | integer | Швидкість порту. |
| dll_path | varchar | Шлях до DLL. |
| ole_progid | varchar | ProgID OLE. |
| status | varchar | Поточний стан. |
| current_shift_id | uuid | Поточна зміна. |
| is_active | boolean | Активність. |
17.2. rro_shifts
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID зміни. |
| device_id | uuid | ID РРО. |
| cashier_id | varchar | Касир. |
| status | varchar | OPEN, CLOSED, ERROR. |
| opened_at | timestamp | Дата відкриття. |
| closed_at | timestamp | Дата закриття. |
| z_report_number | varchar | Номер Z-звіту. |
| raw_open_response | jsonb/text | Відповідь відкриття. |
| raw_close_response | jsonb/text | Відповідь закриття. |
| error_message | text | Помилка. |
17.3. rro_receipts
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID чека. |
| device_id | uuid | РРО. |
| shift_id | uuid | Зміна. |
| external_order_id | varchar | ID замовлення. |
| external_payment_id | varchar | ID оплати. |
| idempotency_key | varchar | Ключ дедублікації. |
| receipt_type | varchar | sale, refund, service. |
| status | varchar | Статус. |
| total_amount | numeric | Загальна сума. |
| fiscal_number | varchar | Фіскальний номер або номер чека, якщо доступний. |
| printed_at | timestamp | Дата друку. |
| raw_command | text/jsonb | Команда до РРО. |
| raw_response | text/jsonb | Відповідь РРО. |
| error_code | varchar | Код помилки. |
| error_message | text | Повідомлення помилки. |
17.4. rro_receipt_items
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID позиції. |
| receipt_id | uuid | ID чека. |
| name | varchar | Назва товару. |
| sku | varchar | Артикул. |
| quantity | numeric | Кількість. |
| unit | varchar | Одиниця. |
| price | numeric | Ціна. |
| amount | numeric | Сума. |
| tax_group | varchar | Податкова група. |
| department | integer | Відділ. |
| discount_amount | numeric | Знижка. |
17.5. rro_events
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID події. |
| entity_type | varchar | device, shift, receipt. |
| entity_id | uuid | ID сутності. |
| event_type | varchar | Тип події. |
| old_status | varchar | Старий статус. |
| new_status | varchar | Новий статус. |
| payload | jsonb/text | Дані події. |
| created_at | timestamp | Дата події. |
18. API Python Agent
18.1. Перевірка стану агента
GET /api/v1/health
18.2. Перевірка стану РРО
GET /api/v1/rro/status
18.3. Відкриття зміни
POST /api/v1/rro/shifts/open
18.4. Закриття зміни / Z-звіт
POST /api/v1/rro/reports/z
18.5. X-звіт
POST /api/v1/rro/reports/x
18.6. Чек продажу
POST /api/v1/rro/receipts/sale
18.7. Чек повернення
POST /api/v1/rro/receipts/refund
18.8. Службова операція
POST /api/v1/rro/service-operation
18.9. Повторити чек
POST /api/v1/rro/receipts/{receipt_id}/retry
18.10. Отримати журнал подій
GET /api/v1/rro/events?date_from=2026-05-01&date_to=2026-05-07
19. Приклад Python-логіки
19.1. Абстрактний інтерфейс драйвера
from abc import ABC, abstractmethod
class RRODriver(ABC):
@abstractmethod
def connect(self) -> None:
pass
@abstractmethod
def get_status(self) -> dict:
pass
@abstractmethod
def open_shift(self, cashier_id: str) -> dict:
pass
@abstractmethod
def close_shift(self) -> dict:
pass
@abstractmethod
def x_report(self) -> dict:
pass
@abstractmethod
def sale_receipt(self, receipt: dict) -> dict:
pass
@abstractmethod
def refund_receipt(self, receipt: dict) -> dict:
pass
@abstractmethod
def service_cash_in(self, amount: float, comment: str | None = None) -> dict:
pass
@abstractmethod
def service_cash_out(self, amount: float, comment: str | None = None) -> dict:
pass
19.2. Приклад OLE-драйвера
class MiniFP54OleDriver(RRODriver):
def __init__(self, ole_progid: str):
self.ole_progid = ole_progid
self.device = None
def connect(self) -> None:
import win32com.client
self.device = win32com.client.Dispatch(self.ole_progid)
def get_status(self) -> dict:
# Назви методів залежать від документації OLE-сервера виробника.
# Тут наведено тільки архітектурний приклад.
result = self.device.GetStatus()
return {"raw": result}
def open_shift(self, cashier_id: str) -> dict:
result = self.device.OpenShift(cashier_id)
return {"raw": result}
def close_shift(self) -> dict:
result = self.device.ZReport()
return {"raw": result}
def x_report(self) -> dict:
result = self.device.XReport()
return {"raw": result}
def sale_receipt(self, receipt: dict) -> dict:
self.device.OpenReceipt(0)
for item in receipt["items"]:
self.device.Sale(
item["name"],
float(item["price"]),
float(item["quantity"]),
item["tax_group"],
item.get("department", 1),
)
for payment in receipt["payments"]:
self.device.Payment(payment["type"], float(payment["amount"]))
result = self.device.CloseReceipt()
return {"raw": result}
Важливо: назви методів OLE/DLL у прикладі є умовними. Реальні назви методів, параметри та коди відповідей потрібно взяти з актуального керівництва програміста / OLE-сервера виробника.
19.3. Приклад Serial-драйвера
class MiniFP54SerialDriver(RRODriver):
def __init__(self, port: str, baud_rate: int, timeout: int = 30):
self.port = port
self.baud_rate = baud_rate
self.timeout = timeout
self.serial = None
def connect(self) -> None:
import serial
self.serial = serial.Serial(
port=self.port,
baudrate=self.baud_rate,
timeout=self.timeout,
)
def send_command(self, command: bytes) -> bytes:
if self.serial is None or not self.serial.is_open:
self.connect()
self.serial.write(command)
response = self.serial.read_until()
return response
def get_status(self) -> dict:
command = b"STATUS_COMMAND_PLACEHOLDER"
response = self.send_command(command)
return {"raw": response.hex()}
def open_shift(self, cashier_id: str) -> dict:
command = b"OPEN_SHIFT_COMMAND_PLACEHOLDER"
response = self.send_command(command)
return {"raw": response.hex()}
def close_shift(self) -> dict:
command = b"Z_REPORT_COMMAND_PLACEHOLDER"
response = self.send_command(command)
return {"raw": response.hex()}
def x_report(self) -> dict:
command = b"X_REPORT_COMMAND_PLACEHOLDER"
response = self.send_command(command)
return {"raw": response.hex()}
Заборонено: використовувати placeholder-команди в production. Для serial-інтеграції обов'язково потрібен офіційний протокол обміну з точним форматом команд, відповідей, кодування та контрольних сум.
20. Обробка помилок
20.1. Типи помилок
| Тип | Опис | Дія системи |
|---|---|---|
| ValidationError | Некоректні дані чека. | Не відправляти на РРО. |
| ConnectionError | Немає зв'язку з РРО. | Перевести чек у CONNECTION_ERROR або NEEDS_RETRY. |
| DriverError | Помилка OLE/DLL/драйвера. | Записати raw-помилку, повідомити адміністратора. |
| PortBusyError | COM-порт зайнятий. | Повторити після паузи або заблокувати чергу. |
| PaperOutError | Немає паперу. | Зупинити друк, показати помаранчевий статус. |
| CoverOpenError | Кришка відкрита. | Зупинити друк, чек лишити в NEEDS_RETRY. |
| FiscalMemoryError | Помилка фіскальної пам'яті. | Критична помилка, заборонити друк. |
| ShiftClosedError | Зміна закрита. | Відкрити зміну, якщо дозволено. |
| DuplicateReceiptError | Чек уже надруковано. | Повернути існуючий результат. |
| RefundLimitError | Повернення перевищує доступну суму. | Заборонити операцію. |
20.2. Retry-логіка
Retry дозволений для:
- тимчасової втрати зв'язку;
- зайнятого COM-порту;
- timeout;
- тимчасової помилки драйвера;
- очікування готовності РРО;
- відновлення після відсутності паперу, якщо чек не був завершений.
Retry заборонений для:
- уже фіскалізованого чека;
- повернення понад доступну суму;
- некоректної суми;
- помилки фіскальної пам'яті;
- невідомого стану, коли неможливо визначити, чи чек уже надруковано.
Критично важливо: якщо після збою неможливо визначити, чи чек був надрукований, система повинна перевести операцію в статус MANUAL_REVIEW, а не автоматично друкувати повторно.
21. Dashboard керівника
21.1. Основні KPI
| KPI | Опис | Колір |
|---|---|---|
| Чеків за день | Кількість чеків продажу. | Інформація |
| Фіскалізовано | Кількість успішних чеків. | Норма |
| Повернення | Кількість чеків повернення. | Контроль |
| Помилки РРО | Кількість помилкових операцій. | Критично |
| Потребують повтору | Чеки у NEEDS_RETRY. | Потрібна дія |
| Незакриті зміни | Відкриті зміни без Z-звіту. | Потрібна дія |
| РРО не підключені | Пристрої без зв'язку. | Критично |
21.2. Приклад dashboard
| Показник | Значення | Стан |
|---|---|---|
| Чеків за день | 384 | Інформація |
| Фіскалізовано | 378 | Норма |
| Повернення | 9 | Контроль |
| Помилки РРО | 4 | Критично |
| Потребують повтору | 3 | Потрібна дія |
| Незакриті зміни | 1 | Потрібна дія |
21.3. Проблемні операції
| Час | РРО | Замовлення | Сума | Статус | Помилка | Дія |
|---|---|---|---|---|---|---|
| 10:42 | MINI-FP54.01 #001 | ORDER-123 | 570.00 | Помилка | Немає зв'язку з COM-портом | Перевірити підключення |
| 11:05 | MINI-FP54.01 #001 | ORDER-124 | 1200.00 | Потребує повтору | Немає паперу | Замінити папір і повторити |
| 12:10 | MINI-FP54.01 #002 | SHIFT-55 | - | Зміна відкрита | Не закрито Z-звіт | Закрити зміну |
22. Безпека
Система повинна забезпечити:
- доступ до локального агента тільки з дозволених IP або через токен;
- HTTPS або локальну захищену мережу;
- авторизацію запитів від K2 ERP / POS;
- розмежування прав: продаж, повернення, X-звіт, Z-звіт, службові операції;
- журнал дій користувачів;
- захист від дублювання чеків;
- заборону прямого доступу до драйвера з кількох процесів;
- шифрування конфігурацій, якщо містять чутливі дані;
- маскування персональних даних покупців у логах.
23. Логування та аудит
Система повинна логувати:
| Подія | Що зберігати |
|---|---|
| Перевірка РРО | Статус, помилки, час. |
| Відкриття зміни | Касир, час, відповідь РРО. |
| Чек продажу | Замовлення, сума, позиції, статус. |
| Чек повернення | Первинний чек, сума, причина. |
| Службова операція | Тип, сума, касир. |
| X-звіт | Час, РРО, відповідь. |
| Z-звіт | Час, номер звіту, результат. |
| Помилка драйвера | Код, текст, raw-відповідь. |
| Повторна операція | Хто запустив, причина, результат. |
24. Acceptance Criteria
24.1. Підключення
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-1 | Адміністратор налаштовує РРО. | Пристрій зберігається в системі. |
| AC-2 | Python Agent перевіряє статус РРО. | Система повертає READY або конкретну помилку. |
| AC-3 | РРО не підключений. | Система показує DISCONNECTED червоним кольором. |
24.2. Продаж
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-4 | POS передає продаж. | Python Agent створює чек у статусі PENDING. |
| AC-5 | РРО готовий. | Чек друкується і переходить у FISCALIZED. |
| AC-6 | Немає паперу. | Чек переходить у NEEDS_RETRY або RRO_ERROR. |
| AC-7 | Повторний запит має той самий idempotency_key. | Другий чек не друкується. |
24.3. Повернення
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-8 | Касир створює повернення. | Друкується чек повернення. |
| AC-9 | Сума повернення перевищує продаж. | Система блокує операцію. |
| AC-10 | Повернення часткове. | Система зменшує доступний залишок до повернення. |
24.4. Зміни та звіти
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-11 | Касир відкриває зміну. | РРО переходить у стан SHIFT_OPEN. |
| AC-12 | Касир формує X-звіт. | РРО друкує X-звіт без закриття зміни. |
| AC-13 | Касир формує Z-звіт. | РРО закриває зміну. |
| AC-14 | Зміна не закрита наприкінці дня. | Dashboard показує помаранчеве попередження. |
24.5. Dashboard
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-15 | Керівник відкриває dashboard. | Він бачить чеки, повернення, помилки, незакриті зміни. |
| AC-16 | Є помилки РРО. | Вони підсвічуються червоним. |
| AC-17 | Є чеки, що потребують повтору. | Вони підсвічуються помаранчевим. |
25. MVP
До MVP входить:
- локальний Python Agent;
- налаштування підключення до МІНІ-ФП54.01;
- перевірка стану РРО;
- відкриття зміни;
- друк чека продажу;
- друк чека повернення;
- службове внесення / винесення;
- X-звіт;
- Z-звіт;
- локальна БД чеків;
- дедублікація;
- журнал команд і відповідей;
- базовий dashboard API;
- обробка помилок паперу, порту, драйвера, зміни;
- retry для безпечних ситуацій.
До MVP не входить:
- повна реалізація всього протоколу обміну;
- автоматичне програмування всієї номенклатури;
- повний POS UI;
- власна фіскальна логіка замість РРО;
- складна офлайн-синхронізація;
- інтеграція з усіма моделями РРО;
- підтримка Linux, якщо обрано OLE/DLL для Windows.
26. Етапи реалізації
Етап 1. Аналіз драйвера та протоколу
- завантажити інструкцію з експлуатації;
- завантажити зміни до протоколу обміну;
- завантажити OLE/DLL-бібліотеку;
- завантажити USB-драйвер;
- перевірити підключення РРО до ПК;
- визначити робочий сценарій: OLE/DLL або serial.
Етап 2. Локальний Python Agent
- створити FastAPI-сервіс;
- реалізувати healthcheck;
- реалізувати локальну БД;
- реалізувати модель пристрою;
- реалізувати логування.
Етап 3. Драйверний шар
- реалізувати RRODriver interface;
- реалізувати MiniFP54OleDriver або MiniFP54SerialDriver;
- реалізувати check_status;
- реалізувати open_shift;
- реалізувати X/Z-звіти.
Етап 4. Чеки
- реалізувати sale receipt;
- реалізувати refund receipt;
- реалізувати валідацію;
- реалізувати дедублікацію;
- реалізувати чергу друку.
Етап 5. Службові операції
- реалізувати cash_in;
- реалізувати cash_out;
- реалізувати права доступу;
- реалізувати аудит.
Етап 6. Dashboard і синхронізація
- реалізувати dashboard API;
- реалізувати список помилок;
- реалізувати синхронізацію з K2 ERP;
- реалізувати експорт журналу, якщо потрібно.
Етап 7. Production hardening
- реалізувати Windows Service;
- додати моніторинг агента;
- додати auto-restart;
- додати резервне копіювання локальної БД;
- додати alerting;
- протестувати типові помилки РРО.
27. Ризики
| Ризик | Опис | Як зменшити |
|---|---|---|
| Немає актуального протоколу | Без документації неможливо безпечно реалізувати serial-інтеграцію. | Використовувати OLE/DLL або отримати документацію виробника. |
| OLE/DLL працює тільки на Windows | Обмеження для Linux-серверів. | Виносити інтеграцію в локальний Windows Agent. |
| Дублювання чеків | Повторний запит може надрукувати другий чек. | Idempotency key, локальна БД, журнал статусів. |
| Невідомий стан після збою | Невідомо, чи чек надрукований. | MANUAL_REVIEW замість автоматичного повтору. |
| Немає паперу | Чек не може бути надрукований. | Статус PAPER_OUT, повтор після заміни паперу. |
| COM-порт зайнятий | Інший процес використовує РРО. | Заборонити паралельний доступ. |
| Не закрито зміну | Касир забув зробити Z-звіт. | Dashboard, нагадування, блок попереджень. |
28. Відкриті питання
- Який варіант інтеграції обираємо: OLE/DLL чи прямий serial-протокол?
- На якій ОС працюватиме касовий ПК: Windows чи Linux?
- Чи потрібно запускати Python Agent як Windows Service?
- Чи потрібно інтегрувати агент із K2 ERP?
- Чи потрібна локальна БД на касовому ПК?
- Чи потрібна офлайн-робота при недоступності центральної ERP?
- Чи потрібно програмувати товари в РРО?
- Чи потрібно програмувати податкові ставки з Python?
- Чи потрібно друкувати QR-код у чеку?
- Чи потрібно відкривати грошову скриньку?
- Чи буде декілька РРО на одному ПК?
- Чи буде один РРО на декілька касирів?
- Чи потрібен централізований dashboard по декількох торгових точках?
- Які типи оплат підтримуються: готівка, картка, змішана оплата?
- Чи потрібна інтеграція з банківським POS-терміналом?
29. Джерела
- https://unisystem.ua/catalog/fiskalnye-registratory/fiskalnyj-registrator-mini-fp54-01/
- Інструкція з експлуатації МІНІ-ФП54.01.
- Зміни до протоколу обміну.
- OLE/DLL-бібліотека виробника.
- USB-драйвер виробника.
- ПЗ UNI-PROGress.
- ПЗ Uniq Commander.