Перейти до вмісту

Інтеграція РРО в Python

Матеріал з K2 ERP Wiki

18. API Python Agent

MINI_FP54_COM_PORT=COM3


3. |- | Кількість товарів | 16 384. №

self.timeout = timeout
allow_service_operations: bool = True

18.7. Чек повернення


! |- | printed_at | timestamp | Дата друку. |- | Shift | Касова зміна. |- | Підключення до ПК | USB, RS232. |}

</syntaxhighlight> Retry дозволений для: ! | style="background:#f3e5f5;" | Контроль |- | Помилки РРО | Кількість помилкових операцій.=== 8.8. X-звіт ===

  • реалізувати cash_in;
  • реалізувати cash_out;
  • реалізувати права доступу;
  • реалізувати аудит. Передача даних до ДПС самим РРО
],

! SEO-опис

response = self.send_command(command)
"sku": "DELIVERY",

1. |- | Контрольна стрічка | КСЕФ / КЛЕФ. Статус

! # Чи потрібно друкувати QR-код у чеку? |- | FiscalMemoryError | Помилка фіскальної пам'яті. | style="background:#eeeeee;" | Сірий |- | Повернення | REFUNDED | По чеку створено повне або часткове повернення. Стан

! |}

def get_status(self) -> dict:
def print_x_report(self) -> "XReportResponse":

Варіант 1. 4.1. Через OLE/DLL-бібліотеку виробника

{

! |}

response = self.send_command(command)

20. Обробка помилок

6. Технологія

result = self.device.GetStatus()

Логіка:

! OLE/DLL або Serial Protocol Python Agent повинен уміти перевіряти: ! |- | event_type | varchar | Тип події. |- | fiscal_number | varchar | Фіскальний номер або номер чека, якщо доступний. |- | cashier_id | varchar | Касир. |}

18.4. Закриття зміни / Z-звіт

Endpoint:

!

|- | 10:42 | MINI-FP54.01 #001 | ORDER-123 | 570.00 | style="background:#ef9a9a;" | Помилка | Немає зв'язку з COM-портом | Перевірити підключення |- | 11:05 | MINI-FP54.01 #001 | ORDER-124 | 1200.00 | style="background:#ffcc80;" | Потребує повтору | Немає паперу | Замінити папір і повторити |- | 12:10 | MINI-FP54.01 #002 | SHIFT-55 | - | style="background:#ffcc80;" | Зміна відкрита | Не закрито Z-звіт | Закрити зміну |}

auto_open_shift: bool = True
* доступ до локального агента тільки з дозволених IP або через токен;
* HTTPS або локальну захищену мережу;
* авторизацію запитів від K2 ERP / POS;
* розмежування прав: продаж, повернення, X-звіт, Z-звіт, службові операції;
* журнал дій користувачів;
* захист від дублювання чеків;
* заборону прямого доступу до драйвера з кількох процесів;
* шифрування конфігурацій, якщо містять чутливі інформаційні дані;
* маскування персональних даних покупців у логах. |-
| external_order_id
| varchar
| ID замовлення. | Заборонити операцію. |}

 pass

=== Етап 2. Локальний Python Agent ===
Приклад:
До MVP входить:
! |-
| AC-2
| Python Agent перевіряє статус РРО. |-
| конкурентні переваги
| Менше залежності від COM/OLE, потенційно кросплатформено. # Чи потрібна офлайн-робота при недоступності центральної ERP? |-
| discount_amount
| numeric
| Знижка. я хочу передати продаж у Python Agent, 
== 2. Область сценарії використання ==
 @abstractmethod

'''критично:''' для МІНІ-ФП54.01 виробник надає USB-драйвер. |-
| print_qr
| boolean
| Чи друкувати QR-код. Окремо варто відзначити OLE/DLL-бібліотеку для фіскальних реєстраторів, інструкції і документи щодо протоколу обміну. |-
| Невідомий стан після збою
| Невідомо, чи чек надрукований. |-
| style="background:#f3e5f5;" | Фіолетовий
| #f3e5f5
| Повернення або спеціальна операційна дія. |-
| is_active
| boolean
| Так
| Чи активний пристрій. Очікуваний результат

 def open_shift(self, cashier_id: str) -> "ShiftResponse":
 @abstractmethod
 allow_refunds: bool = True
=== 17.2. rro_shifts ===
! |-
| external_payment_id
| varchar
| ID оплати. Сума
<pre>
 self.serial.write(command)

</pre>

 pass
<syntaxhighlight lang="json">
{| class="wikitable"
Central Fiscal API
|-
| external_order_id
| string
| ID замовлення у K2 ERP / POS. | Чернетка, зміна закрита. |-
| COM-порт зайнятий
| Інший бізнес-процес використовує РРО. |-
| Нефіскальний друк
| Низький
| Не блокує продажі та реалізація. v
Локальний endpoint:
! | Повторити після паузи або заблокувати чергу. ! |-
| style="background:#ef9a9a;" | Червоний
| #ef9a9a
| Критична помилка. |-
| auto_open_shift
| boolean
| Так
| автоматизовано відкривати зміну перед першим чеком. Сутність
 def close_shift(self) -> "ZReportResponse":
=== 7.1. Продаж ===

'''Критично критично:''' чек повернення не повинен перевищувати залишок по первинному чеку. |-
| Bluetooth
| Опція. SEO-опис
== 11. Єдина логіка кольорів ==
 response = self.send_command(command)
|-
| Чернетка
| DRAFT
| Чек створено в Python-сервісі, але не відправлено на РРО. |-
| serial_number
| varchar
| Серійний номер. Критерій
 self.device.Payment(payment ["type"], float(payment ["amount"]))
! |-
| style="background:#fff9c4;" | Жовтий
| #fff9c4
| Очікування або попередження. Помилка
=== 21.2. Приклад dashboard ===
<pre>

 pass

== 8. Функціональні вимоги ==
{| class="wikitable"
== 3. Особливості моделі МІНІ-ФП54.01 ==

 self.ole_progid = ole_progid

 "tax_group": "NO_VAT",
 return {"raw": result}
Як керівник або адміністратор, 

MINI_FP54_CONNECTION_TYPE=OLE_DLL

щоб коректно повернути кошти покупцю та відобразити операцію в РРО. Для serial-інтеграції обов'язково потрібен офіційний протокол обміну з точним форматом команд, відповідей, кодування та контрольних сум. |-
| RRO Command
| Команда, що відправляється до фіскального реєстратора. |-
| Чек повернення
| Первинний чек, сума, причина. Worker друкує чек. | Він бачить чеки, повернення, помилки, незакриті зміни. ! |-
| receipt_type
| varchar
| sale, refund, service. |-
| Повторна операційна дія
| Хто запустив, причина, результат.=== 17.4. rro_receipt_items ===
! Друк та фіскалізація чека
{| class="wikitable"
{| class="wikitable"
 |
 | 5. |-
| idempotency_key
| string
| Ключ захисту від дублювання. |-
| Немає паперу
| Чек не здатна бути надрукований. | style="background:#ef9a9a;" | Критично
|}

<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">

 "unit": "шт"
9. Перевірити відкриту зміну. | Записати raw-помилку, повідомити адміністратора. baud_rate: int = 115200
[[Категорія:Інтеграції]]
 "name": "Товар 1",

! |-
| RRO Response
| Відповідь пристрою. |-
| z_report_number
| varchar
| Номер Z-звіту. |}

 return {"raw": response.hex()}

 command = b"X_REPORT_COMMAND_PLACEHOLDER"
Python взаємодіє з OLE/COM або DLL-бібліотекою, яку надає виробник. float(item ["quantity"]),

! class MiniFP54SerialDriver(RRODriver):

=== 24.4. Зміни та звіти ===

* фізичних магазинів;
* аптек;
* кафе, барів, ресторанів;
* кіосків;
* торгових точок із обмеженим простором;
* виїзної торгівлі;
* кур'єрської доставки;
* інтернет-магазинів із друком фіскального чека на фізичному РРО;
* POS-вузлів, де потрібна робота з реальним фіскальним реєстратором. Поле
</pre>
<pre>
|-
| Перевірка РРО
| Статус, помилки, час. Передати результат у K2 ERP. # Чи потрібно програмувати податкові ставки з Python?<pre>
|-
| Підходить для
| Глибокої інтеграції, Linux/Windows-сценаріїв, embedded POS. |-
| unit
| varchar
| Одиниця. |-
| Ширина чекової стрічки
| 58 мм. Очікуваний результат

! |}

'''Критично критично:''' якщо після збою неможливо визначити, чи чек був надрукований, платформа повинна перевести операцію в статус MANUAL_REVIEW, а не автоматизовано друкувати повторно. | РРО друкує X-звіт без закриття зміни. ! |-
| idempotency_key
| базовий ключ повторного запиту. | style="background:#ffcc80;" | Помаранчевий
|-
| Відкрита кришка
| COVER_OPEN
| Кришка принтера відкрита. SEO-опис
{| class="wikitable"
! |-
| Передача даних у ДПС
| Через Ethernet або GSM/GPRS-модем. |-
| quantity
| numeric
| Кількість. |-
| Python Agent
| Локальний сервіс на касовому ПК. Перед розробкою потрібно завантажити та перевірити актуальні версії цих компонентів. SEO-опис
 "quantity": 1,

 "department": 1,

{

! |-
| device_model
| string
| Так
| МІНІ-ФП54.01. POST /api/v1/rro/receipts/sale

 return {"raw": result}

* приймати HTTP-запити від K2 ERP / POS;
* керувати РРО;
* виконувати друк чеків;
* повертати статуси;
* зберігати локальний журнал;
* працювати навіть при тимчасовій недоступності центральної системи, якщо це дозволено сценарієм;
* синхронізувати результати з центральною БД. Приклад `.env`:
POST /api/v1/rro/receipts/{receipt_id}/retry
! Поле
! Колір
! |-
| Чек продажу
| Замовлення, сума, позиції, статус. | Вони підсвічуються помаранчевим. Пріоритет

! |-
| Службова операційна дія
| Тип, сума, касир. |}

! Краще використовувати локальний Python Agent біля РРО, а K2 ERP функціонує з ним через API. | Пристрій зберігається в системі. Retry заборонений для:

* наявність external_order_id;
* наявність idempotency_key;
* відсутність уже фіскалізованого чека з таким ключем;
* наявність відкритої зміни або можливість її відкрити;
* готовність РРО;
* наявність паперу;
* відсутність критичних помилок;
* наявність хоча б однієї позиції;
* коректність кількості;
* коректність ціни;
* коректність суми рядка;
* відповідність total_amount сумі товарів і оплат;
* коректність типу оплати;
* коректність податкових груп;
* довжину назви товару;
* довжину рядка чека;
* наявність відділу, якщо він обов'язковий;
* коректність QR-коду, якщо він друкується. Параметр
 item ["name"],
=== 20.1. Типи помилок ===
|-
| AC-8
| Касир створює повернення. |-
| Друк QR / штрих-коду
| Підтримується. |-
| OLE/DLL функціонує тільки на Windows
| Обмеження для Linux-серверів. |}

POST /api/v1/rro/reports/x МІНІ-ФП54.01

return {"raw": response.hex()}
def close_shift(self) -> dict:
pass

5. Де застосовується для POST /api/v1/rro/shifts/open

return {"raw": result}

! Тип

  • створити FastAPI-сервіс;
  • реалізувати healthcheck;
  • реалізувати локальну БД;
  • реалізувати модель пристрою;
  • реалізувати логування. |-

| Дублювання чеків | Повторний запит здатна надрукувати другий чек. |- | RRO Error | Помилка пристрою, драйвера або з'єднання. |- | tax_profile_id | string | Ні | Профіль податкових ставок. pass

v
@abstractmethod


! | style="background:#c8e6c9;" | Зелений |- | Зміна закрита | SHIFT_CLOSED | Перед продажем потрібно відкрити зміну. Для часткових повернень платформа повинна вести залишок доступної до повернення суми та кількості. Код |- | id | uuid | ID чека. |- | AC-7 | Повторний запит має той самий idempotency_key. * OLE/DLL-бібліотека виробника. |- | PortBusyError | COM-порт зайнятий. |}

8.6. Чек повернення

com_port: str | None = "COM3"
def close_shift(self) -> dict:
return {"raw": result}

|- | AC-11 | Касир відкриває зміну. ! |}

24.1. Підключення

критично: МІНІ-ФП54.01 має обмеження на кількість символів у рядку та в назві товару. Поле

result = self.device.OpenShift(cashier_id)

6. Основні сутності

@abstractmethod

24.3. Повернення

! ! |- | Швидкість друку | 8 рядків/с. |- | device_serial_number | string | Так | Серійний номер пристрою. |- | price | numeric | Ціна. |- | Cashier | Касир, від імені якого виконується операційна дія. |- | Python-підхід | pyserial. |- | baud_rate | integer | Швидкість порту. # Чи потрібно програмувати товари в РРО? Черга, журнал, дедублікація

"amount": 70.00,

! |- | dll_path | varchar | Шлях до DLL. SEO-опис

</syntaxhighlight>
- fiscal_number varchar Фіскальний номер. №
! {| class="wikitable"
 item ["tax_group"],

</div>
 "price": 250.00,
'''Критично критично:''' це інтеграційні функції ERP з фізичним РРО, а не з хмарним ПРРО. | Python Agent створює чек у статусі PENDING. Результат синхронізується з центральною системою. Python Agent виконує валідацію. Компонент
{| class="wikitable"
 "amount": 500.00,
=== 8.3. Відкриття зміни ===

Локальний endpoint:
 dll_path: str | None = None
retry_count: int = 2

POST /api/v1/rro/shifts/open

"quantity": 2,

}

6. for payment in receipt ["payments"]:
Поле 4. Тип задачі

21.3. Проблемні операції

def connect(self) -> None:

float(item ["price"]),
pass

У K2 ERP або локальному агенті повинна бути картка РРО. SEO-опис

7.3. Відкриття зміни

@abstractmethod
1. pass
def print_non_fiscal_text(self, lines: list [str]) -> "PrintResponse":
"cashier_id": "cashier-001",
{
for item in receipt ["items"]:

До MVP не входить: щоб закрити касову зміну. # Чи потрібна локальна БД на касовому ПК? | Вони підсвічуються червоним. | style="background:#ffcc80;" | Потрібна дія

Незакриті зміни Відкриті зміни без Z-звіту. Він повинен повернути результат уже виконаної операції. Очікуваний результат

15. Дедублікація

Зелений #c8e6c9 - department integer - AC-5 РРО готовий. Критерій
self.baud_rate = baud_rate

12.3. Методи Python RRO Client

Рекомендована технічна архітектура: Python POS Adapter функціонує локально на комп'ютері касира або POS-вузлі, має доступ до USB/RS232/COM/OLE/DLL-драйвера та приймає команди від ERP через HTTP API або локальну чергу. Тип Критично критично: перед друком фіскального чека агент повинен перевірити готовність РРО. KPI

12.2. Рекомендований стек агента

}

18.5. X-звіт

18.1. Перевірка стану агента

@abstractmethod
"print_qr": true
v

Як касир або адміністратор,

Не відправляти на РРО. |- status varchar Поточний стан. return response

Критично критично: Ethernet і GSM/GPRS у цій моделі використовуються для передачі даних РРО до ДПС, але інтеграційні функції ERP з Python для команд друку чеків зазвичай потребує USB/RS232 або драйвера/OLE/DLL на локальному комп'ютері. Коментар

Створюється локальний запис receipt зі статусом PENDING. pass
log_raw_commands: bool = True
- AC-17 - AC-13 Касир формує Z-звіт.
class MiniFP54Client:
!
Поле

Перед відправкою на РРО платформа повинна перевірити:

- raw_open_response jsonb/text Відповідь відкриття. pass

7.4. Закриття зміни

21. Dashboard керівника

Значення

Рекомендовано для MVP: починати з OLE/DLL-бібліотеки виробника, якщо вона стабільно функціонує з моделлю МІНІ-ФП54.01 та підтримує всі потрібні команди. Тип

self.serial = serial.Serial(
- Блакитний #bbdefb - Помилка драйвера - is_active boolean Активність. def sale_receipt(self, receipt: dict) -> dict:
  • реалізувати dashboard API;
  • реалізувати список помилок;
  • реалізувати синхронізацію з K2 ERP;
  • реалізувати експорт журналу, якщо потрібно. SEO-опис
Чек переходить у NEEDS_RETRY або RRO_ERROR. | платформа повертає READY або конкретну помилку. # Які типи оплат підтримуються: готівка, картка, змішана оплата? Ризик
 v
|-
| RRO Device
| Фізичний фіскальний реєстратор МІНІ-ФП54.01. |-
| Акумулятор
| Вбудована Li-Pol батарея. |-
| CASH_OUT
| Службове винесення готівки. |-
| DriverError
| Помилка OLE/DLL/драйвера. |}

{{SEO
|title=Технічне завдання: Інтеграція РРО МІНІ-ФП54.01 для Python
|description=Технічне завдання на реалізацію Python-сервісу для інтеграції з фізичним фіскальним реєстратором МІНІ-ФП54.01: продажі, повернення, відкриття та закриття зміни, X/Z-звіти, службове внесення/винесення, драйвер, COM/OLE/DLL, USB/RS232, черги, статуси та журналювання.
|keywords=Python, РРО, МІНІ-ФП54.01, MINI-FP54.01, фіскальний реєстратор, Юнісістем, USB, RS232, OLE, DLL, FastAPI, POS, K2 ERP, фіскальний чек, Z-звіт, X-звіт
}}

=== 7.5. Контроль помилок ===

рішення для бізнесу повинно забезпечити:
POST /api/v1/rro/receipts/sale
 pass

! Python Agent повинен або обрізати рядки за правилом, або повертати помилку валідації. | style="background:#c8e6c9;" | Зелений
|-
| Не підключений
| DISCONNECTED
| Немає зв'язку з пристроєм. | платформа блокує операцію. Параметр

Метою задачі виступає як створення Python-рішення для інтеграції з фізичним фіскальним реєстратором '''МІНІ-ФП54.01'''. |-
| original_fiscal_number
| string
| Фіскальний номер первинного чека, якщо доступний. |}

! # Чи потрібно відкривати грошову скриньку? # Чи потрібен централізований dashboard по декількох торгових точках? SEO-опис
<div style="border-left: 6px solid #f57c00; background: #fff3e0; padding: 12px 16px; margin: 16px 0;">
{| class="wikitable"

 result = self.device.XReport()
== 30. Див. так само ==
 def service_cash_out(self, amount: float, comment: str | None = None) -> dict:

POST /api/v1/rro/reports/z
 # Назви методів залежать від документації OLE-сервера виробника. # На якій ОС працюватиме касовий ПК: Windows чи Linux? Параметр
! '''Критично критично:''' повторний запит із тим самим idempotency_key не повинен друкувати другий фіскальний чек. * реалізувати Windows Service;
* додати моніторинг агента;
* додати auto-restart;
* додати резервне копіювання локальної БД;
* додати alerting;
* протестувати типові помилки РРО. |-
| Грошова скринька
| Порт керування micro-jack 2,5 мм.=== 16.1. Логіка черги ===

Як POS або K2 ERP, 
{| class="wikitable"
 },
POST /api/v1/rro/service-operation
|-
| Немає актуального протоколу
| Без документації неможливо безпечно реалізувати serial-інтеграцію. | style="background:#ef9a9a;" | Червоний
|-
| Немає паперу
| PAPER_OUT
| Потрібно замінити рулон. '''Заборонено:''' використовувати placeholder-команди в production. |-
| Z-звіт
| Час, номер звіту, результат. |}

=== 18.2. Перевірка стану РРО ===

# Який варіант інтеграції обираємо: OLE/DLL чи прямий serial-протокол? SEO-опис

2. | Чек друкується і переходить у FISCALIZED. ! | style="background:#fff9c4;" | Жовтий

Відправляється на РРО SENDING_TO_RRO Команда передається в драйвер або COM-порт. Перевірити підключення до РРО. Що зберігати
"unit": "послуга"

щоб агент надрукував і фіскалізував чек на МІНІ-ФП54.01. |}

def service_cash_out(self, amount: float, comment: str | None = None) -> "ServiceOperationResponse":

4. |-

error_code varchar - AC-6 Зупинити друк, чек лишити в NEEDS_RETRY. ! Тип

K2 ERP / POS / CRM / Website

28. Відкриті питання

26. Етапи реалізації

original_receipt_id uuid платформа показує DISCONNECTED червоним кольором. | style="background:#ffcc80;" | Помаранчевий
Зміна відкрита SHIFT_OPEN - com_port string Ні як приклад COM3. Тип
pass

критично: назви методів OLE/DLL у прикладі виступає як умовними. |-

external_payment_id ID оплати. №
baudrate=self.baud_rate,

MINI_FP54_BAUD_RATE=115200

- customer object } style="background:#ffcc80;" | Помаранчевий
Скасовано CANCELLED - Помаранчевий #ffcc80 - Fiscal Receipt - com_port varchar - AC-16 Немає паперу, кришка, повтор. |- Не закрито зміну MANUAL_REVIEW замість автоматичного повтору. Реальні назви методів, параметри та коди відповідей потрібно взяти з актуального керівництва програміста / OLE-сервера виробника. |- AC-3 - Dashboard - AC-12 Касир формує X-звіт. GET /api/v1/health - payments array - total_amount decimal Загальна сума. SEO-опис
- baud_rate integer Ні Швидкість порту, якщо застосовується для serial.
return {"raw": result}
"items": [

Приклад:

"provider": "terminal",
- Відкриття зміни - RRO Agent РРО переходить у стан SHIFT_OPEN. SEO-опис
pass
{

18.8. Службова операційна дія

external_order_id Dashboard, нагадування, блок попереджень. Статус

платформа повинна не допускати дублювання чеків. command = b"Z_REPORT_COMMAND_PLACEHOLDER"

  • уже фіскалізованого чека;
  • повернення понад доступну суму;
  • некоректної суми;
  • помилки фіскальної пам'яті;
  • невідомого стану, коли неможливо визначити, чи чек уже надруковано. |}
- CoverOpenError - tax_group varchar }

1. |-

name varchar - amount numeric - Обмеження Потрібно точно реалізувати протокол, контрольні суми, таймаути, кодування, стани помилок.=== 8.7. Службове внесення / винесення ===
Тип пристрою Фізичний фіскальний реєстратор. def connect(self) -> None:
 v
 def send_command(self, command: bytes) -> bytes:

<pre>

<syntaxhighlight lang="python">
 "amount": 570.00,
 def __init__(self, ole_progid: str):

</div>

{| class="wikitable"
! |-
| Кількість касирів
| 32. ! |-
| DuplicateReceiptError
| Чек уже надруковано. |-
| model
| varchar
| MINI_FP54_01. |-
| sku
| varchar
| Артикул. |-
| Дисплей покупця
| Вбудований 2x16. | Критична помилка, заборонити друк. |-
| Чек повернення
| Високий
| Фінансова операційна дія. ERP надсилає йому HTTP-запити. Як зменшити

* повна реалізація всього протоколу обміну;
* автоматичне програмування всієї номенклатури;
* повний POS UI;
* власна фіскальна логіка замість РРО;
* складна офлайн-синхронізація;
* інтеграційні функції ERP з усіма моделями РРО;
* технічна підтримка Linux, якщо обрано OLE/DLL для Windows. | style="background:#eeeeee;" | Сірий
|-
| Очікує друку
| PENDING
| Чек у черзі на друк. |-
| raw_close_response
| jsonb/text
| Відповідь закриття. |-
| items
| array
| Позиції, які повертаються. |-
| AC-9
| Сума повернення перевищує продаж. SEO-опис

виконати команду відкриття зміни виступає ключовою рисою 4. '''Критично критично:''' Z-звіт виступає як операцією закриття зміни. Перевірити, чи зміна вже відкрита. Зберегти локальний статус. Призначення
|-
| Мова
| 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
|}

{| class="wikitable"

 }
{| class="wikitable"
class RRODriver(ABC):
Мінімальні інформаційні дані:

 def connect(self) -> None:
<syntaxhighlight lang="python">

== 5. Загальна технічна архітектура ==
 response = self.serial.read_until()

__TOC__
class MiniFP54Settings(BaseSettings):

Мінімальні інформаційні дані:
платформа повинна забезпечити:

 @abstractmethod
 pass

{| class="wikitable"
<syntaxhighlight lang="json">

=== 17.3. rro_receipts ===
3. я хочу створити чек повернення, 
 return {"raw": response.hex()}
1. |-
| old_status
| varchar
| Старий статус. |-
| ShiftClosedError
| Зміна закрита. |-
| Refund Receipt
| Чек повернення. Типи:
 "payments": [

8. Зберегти номер і результат Z-звіту. |-
| Обмеження
| Найчастіше потребує Windows, COM/OLE, драйверів і локального доступу до пристрою. | Заборонити паралельний доступ. Замовлення
{| class="wikitable"
|-
| ValidationError
| Некоректні інформаційні дані чека. Значення

! №
from abc import ABC, abstractmethod
 ],
=== 17.5. rro_events ===
|-
| Чеків за день
| Кількість чеків продажу. Перевірити незавершені чеки. |-
| Z Report
| Звіт із закриттям зміни. Worker перевіряє стан РРО. ! №
 self.device = win32com.client.Dispatch(self.ole_progid)
MINI_FP54_AUTO_OPEN_SHIFT=true
!== 23. Логування та аудит ==
 import win32com.client

платформа повинна логувати:
=== Варіант 3. 4.3. Локальний Python Agent + ERP API ===
 def open_shift(self, cashier_id: str) -> dict:

== 7. User Story ==
<div style="border-left: 6px solid #1565c0; background: #e3f2fd; padding: 12px 16px; margin: 16px 0;">
</div>

Він повинен:
! |-
| ConnectionError
| Немає зв'язку з РРО. |-
| entity_id
| uuid
| ID сутності. |-
| created_at
| timestamp
| Дата події. | Виносити інтеграцію в локальний Windows Agent. |-
| entity_type
| varchar
| device, shift, receipt. |-
| X-звіт
| Час, РРО, відповідь. |-
| shift_id
| uuid
| Зміна. SEO-опис
 "cashier_id": "cashier-001",
[[Категорія:МІНІ-ФП54.01]]
! Перевірити стан касира. Тип
 "comment": "Службове внесення на початок зміни",
=== 16.2. Пріоритети ===
 return {"raw": response.hex()}
 @abstractmethod

 "tax_group": "VAT_20",

</div>
</div>

POST /api/v1/rro/receipts/refund

Чек продажу Високий Основна операційна дія. Python RRO Agent — це локальний сервіс, який встановлюється на касовий ПК і має доступ до РРО через USB/RS232/OLE/DLL. Показник
self.device.Sale(
pass
self.serial = None

17.1. rro_devices

2. | style="background:#ef9a9a;" | Червоний

Потребує повтору NEEDS_RETRY style="background:#bbdefb;" | Блакитний
Фіскалізовано FISCALIZED - AC-14 Зміна не закрита наприкінці дня. # Чи потрібна інтеграційні функції ERP з банківським POS-терміналом? Очікуваний результат * ПЗ UNI-PROGress.=== Варіант 2. 4.2. Через прямий протокол обміну RS232/USB-COM === Друкується чек повернення. |- allow_service_operations boolean Так Дозволити службове внесення/винесення. Статус
"idempotency_key": "ORDER-2026-000123-PAY-123456",
"external_order_id": "ORDER-2026-000123",

Логіка:

result = self.device.CloseReceipt()
"operation_type": "CASH_IN",
- cashier_id string Ні - idempotency_key string Ключ захисту від дублювання. Ключ
  • реалізувати RRODriver interface;
  • реалізувати MiniFP54OleDriver або MiniFP54SerialDriver;
  • реалізувати check_status;
  • реалізувати open_shift;
  • реалізувати X/Z-звіти. Тип

я хочу сформувати Z-звіт,

timeout_seconds: int = 30

я хочу відкрити зміну на РРО,

result = self.device.ZReport()
=== 19.2. Приклад OLE-драйвера ===
{| class="wikitable"
<syntaxhighlight lang="python">
 def check_connection(self) -> "RROStatus":
 "sku": "SKU-001",
== 29. Джерела ==
</div>
7. |-
| payments
| array
| Оплати. Час
 |
 | 3. | Перевести чек у CONNECTION_ERROR або NEEDS_RETRY. |-
| CASH_IN
| Службове внесення готівки. * ПЗ Uniq Commander. | style="background:#ffcc80;" | Потрібна дія
|-
| РРО не підключені
| Пристрої без зв'язку. Колір
 timeout=self.timeout,
=== 19.1. Абстрактний інтерфейс драйвера ===
|-
| AC-15
| Керівник відкриває dashboard. |-
| items
| array
| Позиції чека. pass
=== 18.10. Отримати журнал подій ===
=== 8.1. конфігурація пристрою ===
|-
| id
| uuid
| ID зміни. |}

! |-
| Z-звіт
| Критичний
| Закриття зміни. |-
| raw_command
| text/jsonb
| Команда до РРО.<pre>
{| class="wikitable"
 command = b"STATUS_COMMAND_PLACEHOLDER"

POST /api/v1/rro/receipts/refund

=== 21.1. Основні KPI ===

! | style="background:#f3e5f5;" | Фіолетовий
|}

 def __init__(self, port: str, baud_rate: int, timeout: int = 30):

<pre>
! Закрити локальну зміну. Поле
 pass
|-
| Підходить для
| Windows POS, касових робочих місць, локальної інтеграції. | style="background:#ef9a9a;" | Критично
|-
| Потребують повтору
| Чеки у NEEDS_RETRY. Тип

На касовому ПК запускається локальний Python Agent, який функціонує з РРО. | платформа зменшує доступний залишок до повернення. "payment_id": "PAY-123456"

 @abstractmethod

! |}

=== 7.2. Повернення ===

=== 18.3. Відкриття зміни ===

MINI_FP54_RETRY_COUNT=2
</div>
def x_report(self) -> dict:
AC-1 - Сірий #eeeeee - device_id uuid style="background:#e3f2fd;" | інформаційні матеріали
Фіскалізовано - конкурентні переваги - opened_at timestamp style="background:#ffcc80;" | Помаранчевий
Помилка фіскальної пам'яті FISCAL_MEMORY_ERROR Idempotency key, локальна БД, журнал статусів. ! |- Service Operation - Службове внесення / винесення Середній Касова операційна дія. Тип
|-
| id
| uuid
| ID позиції.=== 24.2. Продаж ===
'''критично:''' прямий протокол потрібно реалізовувати тільки після отримання актуальної документації виробника щодо команд, форматів пакетів, контрольних сум і відповідей РРО. | Помилка РРО, порт недоступний, фіскальна помилка. РРО
Ключі дедублікації:

 def open_shift(self, cashier_id: str) -> dict:

=== Етап 5. Службові операції ===
<pre>


 self.device = None
== 14. Валідація чека ==
[[Категорія:РРО]]
POST /api/v1/rro/reports/z
=== 18.6. Чек продажу ===
Python відкриває COM-порт і відправляє команди згідно з протоколом обміну. Значення / SEO-опис

=== Етап 6. Dashboard і синхронізація ===
== 9. Статуси чеків ==
== 27. Ризики ==

POST /api/v1/rro/reports/x

<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">
 def x_report(self) -> dict:
 self.device.OpenReceipt(0)
POST /api/v1/rro/service-operation
MINI_FP54_TIMEOUT_SECONDS=30
інтеграційні функції ERP призначена для:
== 12. Python RRO Agent ==
[[Категорія:POS]]
Покупець / чекова стрічка

! Дія системи

* отримати проміжний звіт без закриття зміни;
* перевірити обороти;
* перевірити стан каси;
* показати керівнику поточні підсумки. Endpoint:
=== 24.5. Dashboard ===
|-
| Чеків за день
| 384
| style="background:#e3f2fd;" | інформаційні матеріали
|-
| Фіскалізовано
| 378
| style="background:#c8e6c9;" | Норма
|-
| Повернення
| 9
| style="background:#f3e5f5;" | Контроль
|-
| Помилки РРО
| 4
| style="background:#ef9a9a;" | Критично
|-
| Потребують повтору
| 3
| style="background:#ffcc80;" | Потрібна дія
|-
| Незакриті зміни
| 1
| style="background:#ffcc80;" | Потрібна дія
|}

</syntaxhighlight>

GET /api/v1/rro/status
 "total_amount": 570.00,
=== 8.4. Чек продажу ===
Поле
AC-4 Повернути існуючий результат. Продаж / повернення / службова операційна дія HTML
command = b"OPEN_SHIFT_COMMAND_PLACEHOLDER"
 )
 def sale_receipt(self, receipt: dict) -> dict:

<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">

=== Етап 1. Аналіз драйвера та протоколу ===
== 1. Мета ==
! | Черга чеків, очікування друку.<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">

 response = self.send_command(command)
5. |-
| PaperOutError
| Немає паперу. Результат зберігається локально. |-
| Кількість відділів
| 64. SEO-опис

{| class="wikitable"

def service_cash_in(self, amount: float, comment: str | None = None) -> dict:
 def open_shift(self, cashier_id: str) -> dict:
|-
| id
| uuid
| ID пристрою. |-
| РРО МІНІ-ФП54.01
| Фізичний пристрій, підключений через USB/RS232. | Refund, службові операції. |-
| current_shift_id
| uuid
| Поточна зміна. Python-сервіс повинен працювати через драйвер, COM/OLE/DLL, протокол обміну, USB/RS232 або проміжний локальний агент. SEO-опис

! | style="background:#ef9a9a;" | Червоний
|-
| Помилка з'єднання
| CONNECTION_ERROR
| Немає зв'язку з РРО або драйвером. |-
| error_message
| text
| Помилка. |-
| RefundLimitError
| Повернення перевищує доступну суму. |}

 self.connect()

'''Управлінський результат:''' керівник повинен бачити, скільки чеків надруковано, скільки повернень виконано, які зміни відкриті, які Z-звіти сформовані, які РРО мають помилки зв'язку або потребують уваги. pass
 item.get("department", 1),
=== 12.1. Призначення ===
<div style="border-left: 6px solid #f57c00; background: #fff3e0; padding: 12px 16px; margin: 16px 0;">

2. Якщо потрібно — відкриває зміну. Дія

! |-
| device_id
| uuid
| ID РРО. ! |-
| ole_progid
| string
| Ні
| ProgID OLE-сервера, якщо застосовується для OLE. | Використовувати OLE/DLL або отримати документацію виробника. class MiniFP54OleDriver(RRODriver):
</div>
<pre>
Як касир, 
GET /api/v1/rro/events?date_from=2026-05-01&date_to=2026-05-07
 pass
{| class="wikitable"
 def refund_receipt(self, receipt: dict) -> dict:

== 19. Приклад Python-логіки ==

connection_type: str = "OLE_DLL"
def x_report(self) -> dict:

16. Черга друку

4. |-

connection_type enum Так - closed_at timestamp Дата закриття. Критерій

Рекомендована схема для K2 ERP: K2 ERP не повинна напряму керувати COM-портом. Критерій

20.2. Retry-логіка

import serial
self.port = port
Готовий READY РРО готовий до роботи. я хочу бачити помилки РРО,
ole_progid: str | None = None

Етап 3. Драйверний шар

Local Python RRO Agent

13. Приклад конфігурації

"name": "Доставка",
Повернути результат у K2 ERP / POS. Критерій

10. Статуси РРО

)
def get_status(self) -> dict:

}

if self.serial is None or not self.serial.is_open:
Другий чек не друкується. |}

Етап 4. Чеки

def get_status(self) -> dict:

Endpoint:

! |-
| Python-підхід
| pywin32 / comtypes / ctypes залежно від типу бібліотеки. Поле
{| class="wikitable"
</syntaxhighlight>

{| class="wikitable"

{| class="wikitable"
== 4. Варіанти інтеграції Python з РРО ==
 "amount": 1000.00,

 port=self.port,

 def close_shift(self) -> dict:

=== 8.5. Приклад запиту на чек продажу ===

== 17. Модель даних ==
! |-
| status
| varchar
| OPEN, CLOSED, ERROR. Код
 retry_backoff_seconds: int = 3
щоб оперативно реагувати на проблеми з папером, зв'язком, портом, драйвером або фіскалізацією. Тип

<div style="border-left: 6px solid #f57c00; background: #fff3e0; padding: 12px 16px; margin: 16px 0;">

! |-
| X Report
| Проміжний звіт без закриття зміни. |-
| new_status
| varchar
| Новий статус. | style="background:#ef9a9a;" | Червоний
|-
| Немає зв'язку з ДПС
| TAX_SERVER_CONNECTION_ERROR
| Пристрій не здатна передати інформаційні дані. |-
| raw_response
| text/jsonb
| Відповідь РРО. SEO-опис
 "price": 70.00,
! Endpoint:
|-
| device_name
| string
| Так
| Назва РРО. |-
| payload
| jsonb/text
| інформаційні дані події. |-
| error_message
| text
| Повідомлення помилки. | Dashboard показує помаранчеве попередження. |-
| status
| varchar
| Статус. | style="background:#bbdefb;" | Блакитний
|-
| Друкується
| PRINTING
| РРО виконує друк. |-
| dll_path
| string
| Ні
| Шлях до DLL, якщо застосовується для DLL.<pre>

! Компонент

=== 8.9. Z-звіт ===

* локальний Python Agent;
* конфігурація підключення до МІНІ-ФП54.01;
* перевірка стану РРО;
* відкриття зміни;
* друк чека продажу;
* друк чека повернення;
* службове внесення / винесення;
* X-звіт;
* Z-звіт;
* локальна БД чеків;
* дедублікація;
* журнал команд і відповідей;
* базовий dashboard API;
* обробка помилок паперу, порту, драйвера, зміни;
* retry для безпечних ситуацій. |-
| idempotency_key
| varchar
| Ключ дедублікації. Обов'язковість
=== 19.3. Приклад Serial-драйвера ===
== 24. Acceptance Criteria ==
 def print_sale_receipt(self, payload: "SaleReceiptPayload") -> "ReceiptResponse":
 def get_status(self) -> "RROStatus":
sha256(external_order_id + total_amount + payment_id + device_serial_number)
! |-
| X-звіт
| Середній
| Контрольний звіт. ДПС
== 25. MVP ==

{
Призначення:
<pre>

from pydantic_settings import BaseSettings

</div>

 pass
 def print_refund_receipt(self, payload: "RefundReceiptPayload") -> "ReceiptResponse":
 def service_cash_in(self, amount: float, comment: str | None = None) -> "ServiceOperationResponse":
 "department": 2,
=== 8.2. Перевірка стану РРО ===
7. Колір
6. | Dashboard, список чеків, статус РРО. * USB-драйвер виробника. |-
| cashier_id
| string
| Касир. |-
| fiscal_number
| string
| Так
| Фіскальний номер РРО.[[Категорія:K2 ERP]]
 "idempotency_key": "CASH-IN-2026-05-07-001"
|-
| id
| uuid
| ID події. MINI_FP54_LOG_RAW_COMMANDS=true

* https://unisystem.ua/catalog/fiskalnye-registratory/fiskalnyj-registrator-mini-fp54-01/
* Інструкція з експлуатації МІНІ-ФП54.01. |-
| receipt_hash
| Hash товарів, сум, оплат і каси. |-
| connection_type
| varchar
| OLE_DLL, SERIAL, USB_COM. SEO-опис
! | Черга, друк, передача команди. |-
| AC-10
| Повернення часткове. |-
| ole_progid
| varchar
| ProgID OLE.<syntaxhighlight lang="python">

* реалізувати sale receipt;
* реалізувати refund receipt;
* реалізувати валідацію;
* реалізувати дедублікацію;
* реалізувати чергу друку. |-
| external_refund_id
| string
| ID повернення в ERP / POS. | style="background:#eeeeee;" | Сірий
|-
| Заблоковано
| BLOCKED
| Робота неможлива. 3. | Зупинити друк, показати помаранчевий статус. |}

</div>

інтеграції ERP / POS / CRM / інтернет-магазину з фізичним фіскальним реєстратором '''МІНІ-ФП54.01''' для друку та фіскалізації чеків забезпечується через '''Головна ідея:''' розробити Python-сервіс або Python-адаптер; так само реалізовано повернень, службових операцій, відкриття і закриття змін.=== 18.9. Повторити чек ===

"type": "CARD",
  • підключення до РРО через USB, RS232 або драйвер;
  • роботу через OLE/DLL-бібліотеку виробника або прямий протокол обміну;
  • перевірку стану РРО;
  • відкриття касової зміни;
  • друк і фіскалізацію чека продажу;
  • друк і фіскалізацію чека повернення;
  • службове внесення готівки;
  • службове винесення готівки;
  • формування X-звіту;
  • формування Z-звіту;
  • друк нефіскального тексту, якщо підтримується;
  • програмування товарів, податкових груп, касирів — якщо потрібно;
  • контроль помилок РРО;
  • журналювання команд і відповідей;
  • захист від дублювання чеків;
  • повторну обробку технічних помилок;
  • інтеграцію з K2 ERP / POS / CRM / сайтом. POS / K2 ERP надсилає запит на чек. | style="background:#ef9a9a;" | Червоний

</syntaxhighlight>

# Чи потрібно запускати Python Agent як Windows Service? * завантажити інструкцію з експлуатації;
  • завантажити зміни до протоколу обміну;
  • завантажити OLE/DLL-бібліотеку;
  • завантажити USB-драйвер;
  • перевірити підключення РРО до ПК;
  • визначити робочий сценарій: OLE/DLL або serial. | style="background:#c8e6c9;" | Норма
Повернення style="background:#c8e6c9;" | Зелений
Помилка РРО RRO_ERROR Відкрити зміну, якщо дозволено. |}

</syntaxhighlight>

Як касир,

  • чи підключений РРО;
  • чи доступний порт;
  • чи доступний драйвер/OLE/DLL;
  • чи виступає як папір;
  • чи відкрита кришка;
  • чи виступає як помилки живлення;
  • чи виступає як зв'язок з ДПС через канали пристрою;
  • чи відкрита зміна;
  • чи не заблокований РРО;
  • чи не переповнена пам'ять;
  • чи коректно встановлена дата і час;
  • чи готовий РРО до друку чека. | Статус PAPER_OUT, повтор після заміни паперу. # Чи буде декілька РРО на одному ПК? Чек потрапляє в чергу друку. * Зміни до протоколу обміну. Подія
5. Якщо РРО має критичну помилку, чек не повинен переходити в статус «Фіскалізовано». Виконати команду формування Z-звіту. | РРО закриває зміну. |}



* тимчасової втрати зв'язку; * зайнятого COM-порту; * timeout; * тимчасової помилки драйвера; * очікування готовності РРО; * відновлення після відсутності паперу, якщо чек не був завершений. |-
reason string Причина повернення. Колір

Етап 7. Production hardening

- total_amount numeric Загальна сума. SEO-опис

22. Безпека

K2 ERP / POS - receipt_id uuid ID чека. Очікуваний результат щоб мати можливість друкувати фіскальні чеки.