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

Вчасно-каса

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

!Роль print(response.status_code)

logger.exception("Fiscal job failed")

Що перевірити

tag: str,
)
9.1.; self._worker.start()
tag = f"K2:SALE:{k2_document ['id']}"

import logging

def fiscal_request( fiscal_data={
  • фіскалізувати чеки продажу;
  • робити чеки повернення;
  • відкривати та закривати зміну;
  • формувати X-звіти та Z-звіти;
  • працювати з оплатами;
  • отримувати результат уже проведеної операції;
  • безпечно повторювати запити при timeout або втраті звʼязку;
  • працювати, коли ПРРО тимчасово переходить в офлайн-режим;
  • логувати всі фіскальні операції у K2 ERP.; |-
Протестовано timeout.; Якщо res_action = 3:
== Production checklist ==

ПРРО Вчасно.Каса / принтер / термінал


{| class="wikitable"


{| class="wikitable"
`tag`  це ідентифікатор операції, який надає можливість:


 "device": device,
</pre>


 "name": "Кава",
=== Етап 1.; Перевірка доступності Device Manager ===


 self._queue.put(job)
 payload = build_sale_payload(k2_document)
 goods.append(
 username: Optional [str] = None
def fiscalize_sale(


 "sku": "482000000002",
</pre>
 job()
class VchasnoKasaError(Exception):
!Дія в K2 ERP
!ОС
!Статус
class VchasnoKasaBusinessError(VchasnoKasaError):


 timeout=request_timeout,
 з актуальною документацією Вчасно.Каса;
 "id": "INV-000123",
=== Шаблон payload для продажу ===

 ├── test_mapper.py
 payload = {

) -> dict:
 finally:

├── queue.py
 ) -> Dict [str, Any]:
 {
<syntaxhighlight lang="python">
 ├── test_tag.py
 return f"{self.config.base_url.rstrip('/')}{path}"
├── device_manager.py

 return {
 "sum": round(float(line ["quantity"]) * float(line ["price"]), 2),
Device Manager має змогу обробляти різні запити паралельно, але для одного конкретного пристрою операції виконуються синхронно.; Для кожної фіскальної операції K2 ERP має передавати унікальний `tag`.;<syntaxhighlight lang="python">
 }
!task_status
 client: VchasnoDeviceManagerClient,

== Рекомендована структура Python-проєкту ==

</syntaxhighlight>

Endpoint
# Тут можуть бути додаткові параметри,
# якщо вони потрібні згідно з актуальною документацією Вчасно.Каса.; |-
- 1122 Закінчились офлайн-номери Дочекатися реєстрації офлайн-чеків та переходу онлайн.;=== Основні API endpoint-и ===
Код
timeout: Optional [int] = None,
характеристика
  • таблиця черги в базі K2 ERP;
  • окрема таблиця `fiscal_queue`;
  • Redis Queue;
  • Celery;
  • RabbitMQ;
  • Kafka, якщо вже застосовують, коли потрібно в інфраструктурі.; |-
Обробка timeout Python-сервіс має змогу повторно перевірити чек за тим самим `tag`.;
 "tax": line.get("tax_group", 1),
Для production краще використовувати не in-memory чергу, а одну з таких схем:
</pre>
 print(result)

 res = data.get("res")
5.; Якщо timeout:

Рекомендована назва тестової каси:<pre>
 "id": "ITEM-001",

# додати тестову ПРРО-касу;
# вказати токен або необхідні параметри Вчасно.Каса;
# налаштувати ключ підпису;
# налаштувати податкові групи;
# налаштувати типи оплат;
# присвоїти касі зрозумілу назву.; |}

== Тестування інтеграції ==
 11.1.; |-
|k2_document_id
|string
|ID документа K2 ERP.; Показати помилку користувачу або адміністратору.; Якщо res_action = 2:
 def put(self, job: Callable [[], None]) -> None:
 goods = []
{

</syntaxhighlight>

client.execute(payload, timeout=1)

інтеграційні функціональні можливості K2 ERP з Вчасно.Каса через Python та Device Manager

11.; |-

1125 Застаріла реліз Device Manager - tag string Унікальний tag операції.; За замовчуванням:
 payments.append(
 ↓
Відкрити у браузері:<pre>
 raise VchasnoKasaTimeoutError(

<pre>
K2 ERP
4.; |-
| style="background:#fff3cd; color:#856404; font-weight:bold;" |□
|Протестовано повторну відправку з тим самим `tag`.; from dataclasses import dataclass
 raise VchasnoKasaError(f"Device Manager HTTP error: {exc}") from exc
== Компоненти інтеграції ==
 tag=tag,
{| class="wikitable"
{| class="wikitable"
!Значення
curl http://localhost:3939
!Поле
== Таблиця fiscal_operations у K2 ERP ==
!Коментар
|-
| style="background:#d4edda; color:#155724; font-weight:bold;" |Обовʼязково
|Один документ K2 ERP = один стабільний `tag` для фіскалізації.; |-
| style="background:#fff3cd; color:#856404; font-weight:bold;" |Arch Linux
|x86_64
|Потрібні додатково `curl` та `jq`.; |-
|<code>1092</code>
|Зміна закрита
|Відкрити зміну перед продажем.; Python-сервіс відправляє POST /dm/execute.; |-
|request_json
|json
|Запит до Device Manager.; |-
|<code>/dm/execute-pkg</code>
|Пакетний режим: ПРРО + термінал + принтер.; # Перевіряємо результат по з цієї причини самому tag.; |-
|`tests/`
|Автоматизовані тести.; як ілюстрація: <code>K2_MAIN_KASA</code>.; Python-сервіс НЕ створює новий tag.; |-
|<code>2</code>
|Чек повернення
|Повернення товару або скасування продажу за процедурою повернення.; !Task
logging.basicConfig(

У вебінтерфейсі Device Manager потрібно:
 "tag": tag,
<syntaxhighlight lang="python">
 tag: str,
!Компонент

 # Назву цього блоку потрібно звірити з актуальним fiscal API.; |-
| style="background:#fff3cd; color:#856404; font-weight:bold;" |Контроль
|Перевіряти помилки `1052`, `1056`, `1062`, `1122`.; |-
|<code>1062</code>
|ПРРО знаходиться в офлайн-режимі понад дозволений час
|Дозволити тільки закриття зміни, звернутись до підтримки.; |-
|created_at
|datetime
|Дата створення.; |}

4.; |}

== Обробка task_status ==
 "type": "cash",
<pre>
 if fiscal_data:
=== Етап 7.; Тест зайнятого пристрою ===


 device: str = "K2_TEST_KASA"
</syntaxhighlight>
try:
 tag=tag,


tag: str,
"name": line ["name"],

Відправка чека продажу

Мета інтеграції

response = requests.get("http://localhost:3939", timeout=5)

Значення
self._worker = threading.Thread(target=self._run, daemon=True)
def execute(

print(result)

result = fiscalize_sale(client, k2_document) logging.info(json.dumps(log_data, ensure_ascii=False)) Рекомендовано створити окрему таблицю для контролю фіскальних операцій.; |-
status string }
"lines": [
url,

10.; |-

2 Колізія - 1054 Неможливо перейти в онлайн, бо ПРРО вже онлайн - Термінал 310 секунд - 2 Знайдено раніше виконану операцію за tag - 2000 Невірний JSON Виправити payload у Python-сервісі.; ├── app.py

Етап 6.; Тест timeout

self._queue.task_done()
"tax_group": 1, 5.; |-
http://localhost:3939/dm/ - - - 1053 Неможливо перейти в офлайн, бо ПРРО вже офлайн Перевірити поточний стан ПРРО.; Підтримуються:

Приклад документа K2 ERP

8.; |-

3 pay }
f"Device Manager timeout after {request_timeout} s"

http://localhost:3939/dm/vchasno-kasa/

device="K2_TEST_KASA",
Правильно }

Висновок

class VchasnoDeviceManagerClient:

Якщо K2 ERP або Python-сервіс знаходиться на іншому пристрої в локальній мережі:

|- | style="background:#d4edda; color:#155724; font-weight:bold;" |□ |Device Manager встановлено.; Формує JSON, надсилає запити, обробляє відповіді, веде лог.; |- | style="background:#f8d7da; color:#721c24; font-weight:bold;" |Не робити |Не створювати чек повторно з новим `tag`, якщо попередній запит завершився timeout |Треба повторити запит із тим самим `tag`, інакше можливий дубль чека.; pass У цій статті описана рекомендована інтеграційні функціональні можливості:<pre> !Причина Перевірка з компʼютера, де встановлено Device Manager:<syntaxhighlight lang="bash">

"""

{| class="wikitable"

"payments": payments,
if res == 1105:
raise VchasnoKasaDeviceBusyError(data.get("errortxt", "Пристрій зайнятий"))
if task_status == 3 or (isinstance(res, int) and res > 0):
return client.fiscal_request(
"device": self.config.device,

<syntaxhighlight lang="bash">

request_payload: dict,
"type": payment ["type"], # cash/card/etc — згідно з налаштуваннями ПРРО

!Device Manager / Вчасно.Каса Для K2 ERP це означає:

{| class="wikitable"

  • усі фіскальні операції мають мати стабільний `tag`;
  • timeout не можна трактувати як однозначну помилку;
  • після timeout потрібно перевіряти результат за тим самим `tag`;
  • на одну касу має бути одна послідовна черга операцій;
  • потрібно обробляти `task_status`, `res`, `res_action`;
  • офлайн-режим потрібно підтримувати на рівні логіки повторів, статусів і звірки;
  • усі запити та відповіді потрібно логувати в K2 ERP.; |-

|operation_type |string |sale, return, open_shift, z_report.; |- | style="background:#fff3cd; color:#856404; font-weight:bold;" |1 |Помилка, можна повторити |Повторити з тим самим `tag`.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |Рекомендовано |Писати окремий Python-сервіс-посередник |Не варто напряму вбудовувати HTTP-виклики Вчасно.Каса у бізнес-логіку K2 ERP.; |- |<code>3</code> |Службове внесення |Внесення готівки в касу.; !Використання в K2 ERP Окремо варто відзначити який встановлюється на компʼютер, сервер або Android-пристрій і надає локальне REST API; додатково реалізовано принтерами, банківськими терміналами і іншими POS-пристроями виступає ключовою рисою роботи з ПРРО Вчасно.; |- | style="background:#fff3cd; color:#856404; font-weight:bold;" |□ |Протестовано втрату інтернету.; from datetime import datetime Device Manager має змогу певний час працювати з ПРРО в офлайн-режимі, якщо виникають проблеми з онлайн-фіскалізацією: недоступність ДПС, проблеми з АЦСК, проблеми з ключем або мережею.; Запит потрапляє в чергу конкретної каси.; errortxt: Пристрій зайнятий import json http://localhost:3939

"tag": tag,

!Призначення

def _validate_response(self, data: Dict [str, Any]) -> None:

) !Рекомендація |- | style="background:#d4edda; color:#155724; font-weight:bold;" |Добре |Використовувати стабільні імена пристроїв: `K2_TEST_KASA`, `K2_MAIN_KASA`.; |}

"quantity": 1,

) -> dict:

def __init__(self, config: DeviceManagerConfig):
}
base_url: str = "http://localhost:3939"

|- |ПРРО: чек, зміна, звіти | style="background:#fff3cd; color:#856404; font-weight:bold;" |20 секунд або більше |Через можливий перехід в офлайн.; # Повторно виконати запит з тим самим `tag`.; # Запустити Device Manager.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |Обовʼязково |Передавати унікальний `tag` для кожної фіскальної операції |Це основа транзакційності та захисту від дублювання чеків.; password: Optional [str] = None |- |Ізоляція інтеграції |K2 ERP не має напряму залежати від формату Device Manager API.; |- |<code>task</code> |Номер конкретного задача.; Нижче Ubuntu 18.04 не підтримується.; У production краще ставити документ у чергу.; |- |Device Manager |Локальний застосунок Вчасно.Каса, який приймає REST API запити.; |- |`errors.py` |Класи помилок інтеграції.; Якщо чек знайдено — записує фіскальний результат у K2 ERP.; |- |<code>2</code> |doc |Друк документів або робота з принтером.; |- | style="background:#fff3cd; color:#856404; font-weight:bold;" |істотно |Timeout для ПРРО — не менше 20 секунд |Потрібно врахувати можливий автоматичний перехід каси в офлайн.; # Перевірити, що черга K2 ERP повторює операцію пізніше.; ├── mapper.py Після встановлення та запуску Device Manager піднімає локальний вебсервер.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |Android |Android 6+ |Рекомендовано Android 10+.; |- |Принтер |Друк нефіскальних або службових документів, якщо застосовується.; |- |<code>1105</code> |Пристрій зайнятий |Повторити через чергу або зачекати завершення попередньої операції.; |- |`models.py` |DTO / Pydantic-моделі.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |Обовʼязково |При повторі після timeout використовувати той самий `tag`.; |- |Масштабування |Можна підключити кілька кас, кожну з власною чергою.; |- | style="background:#fff3cd; color:#856404; font-weight:bold;" |Можливо |При `1105` повторити запит через коротку паузу.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |□ |Назва `device` у K2 ERP збігається з назвою в Device Manager.; |- |Пакетний режим | style="background:#fff3cd; color:#856404; font-weight:bold;" |Сума timeout-ів |ПРРО + термінал + принтер.; "tag": tag,

=== Етап 4.; Тест продажу ===

2.; Повторити останню операцію з тим самим tag.; Якщо res_action = 1: Device Manager повертає `task_status`.; "fiscal": {

↓
format="%(asctime)s %(levelname)s %(message)s",

!Значення

print(result)

|- |<code>1052</code> |ПРРО заборонено переходити в офлайн |Показати помилку касиру, звернутись до адміністратора.; Python-сервіс сформував tag.; {| class="wikitable"

self._validate_response(data)
while True:
pass
10.1.; |}
def __init__(self):

)

return data
self,
)

|- | style="background:#d4edda; color:#155724; font-weight:bold;" |0 |задача пройшло |Зберегти результат.; Виконати пошук за тим самим tag.; @dataclass <pre> === Помилка 1105 ===

logging.error(json.dumps(log_data, ensure_ascii=False))

!характеристика

"price": 50.00,

from typing import Callable

</syntaxhighlight>

except requests.RequestException as exc:
"goods": goods,

<pre> class VchasnoKasaDeviceBusyError(VchasnoKasaError): |- |`device_manager.py` |HTTP-клієнт для Device Manager.; |- |response_json |json |Відповідь Device Manager.; |- |Черга запитів |Для однієї каси запити треба виконувати послідовно.; |- | style="background:#f8d7da; color:#721c24; font-weight:bold;" |Заборонено |Генерувати новий `tag` при повторній відправці того самого чека.; |- |<code>type</code> |Тип задача.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |□ |Обробляється помилка `1105`.; |}

log_data = {
except VchasnoKasaTimeoutError:
  1. Завантажити пакет для потрібної ОС.; |-

|<code>/dm/execute-prn</code> |Виконання операцій друку.; else: !Тип

{| class="wikitable"

filename="k2_vchasno_kasa.log",

!Призначення </syntaxhighlight>

result = open_shift(client, k2_shift_id="SHIFT-2026-05-06-001")

Якщо K2 ERP не отримала відповідь від Device Manager, потрібно не створювати новий чек, а перевірити попередній результат за `tag`.;http://{{dm_ip}}:3939

</syntaxhighlight>

# Пристрій зайнятий.; task=11,
self,
response = self.session.post(

У запитах Device Manager використовуються три ключові поля:

Оскільки Device Manager не виконує паралельні операції на одному ПРРО, у Python-сервісі потрібно зробити чергу.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |□ |Тестову касу додано.; |- |`mapper.py` |Перетворення документів K2 ERP у JSON для Вчасно.Каса.; |- | style="background:#fff3cd; color:#856404; font-weight:bold;" |□ |Протестовано офлайн-режим.; |- |res |int |Код результату Device Manager.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |□ |Реалізовано повторну перевірку за `tag` після timeout.; |- |Офлайн-режим |Python-сервіс має змогу коректно обробити затримки та статуси офлайн-роботи.; # Встановити застосунок.; payload = { } == Офлайн-режим Вчасно.Каса == === Етап 3.; Тест відкриття зміни === K2_TEST_KASA Увага: точні назви полів для товарів, оплат, податкових груп і знижок потрібно звірити з актуальною сторінкою `API для роботи з ПРРО`.; |- |<code>2001</code> |Невірний токен Вчасно.Каса |Оновити токен у налаштуваннях ПРРО.; |}

!Очікуваний результат

self.config = config
}
  • повторно отримати результат уже проведеного чека;
  • не створити дубль при timeout;
  • звірити операції між K2 ERP та Device Manager;
  • відновити стан після збою живлення або мережі.; |-

| style="background:#d4edda; color:#155724; font-weight:bold;" |□ |Device Manager доступний на `http://localhost:3939` або по IP.; |}

def log_operation( !Рекомендований timeout k2_vchasno_integration/

або з іншого пристрою:

|- |<code>/dm/execute</code> |ключовий endpoint для виконання фіскальних операцій по ПРРО.; |}

=== Спрощена in-memory черга ===

"request": request_payload,
payload: Dict [str, Any] = {
"""

|- |K2 ERP |Джерело документів продажу, повернення, оплат, зміни та облікових даних.; |- |ПРРО Вчасно.Каса |Фіскалізація чеків, відкриття/закриття зміни, формування звітів.; |}

"price": round(float(line ["price"]), 2),

import time

return client.find_by_tag(payload ["tag"])
request_timeout = timeout or self.config.fiscal_timeout

def close_shift(client: VchasnoDeviceManagerClient, k2_shift_id: str) -> dict: </syntaxhighlight> |- | style="background:#d4edda; color:#155724; font-weight:bold;" |1 |задача виконано |Позначити операцію як успішну.; K2 ERP створила документ продажу.; |}

</syntaxhighlight>

return client.execute(payload, timeout=client.config.fiscal_timeout)
}

!Коментар res: 1105

) from exc

!рішення для бізнесу

k2_document: dict,

== Короткий висновок ==

time.sleep(2)
"device": k2_document ["device"],

|- |<code>http://localhost:3939/dm/vchasno-kasa/</code> |конфігурація ПРРО Вчасно.Каса.; |- | style="background:#f8d7da; color:#721c24; font-weight:bold;" |□ |Немає повторної фіскалізації з новим `tag` після timeout.; {| class="wikitable"

"operation_type": operation_type,
return self.execute(payload)
try:

!операційна дія === Крок 3.; Додати тестову касу === |- |Номер документа |`tag`, службовий номер або коментар |- |Дата документа |Дата операції |- |Каса / торгова точка |`device` |- |Товар |Рядок чека |- |Кількість |Кількість у рядку чека |- |Ціна |Ціна одиниці |- |Знижка |Знижка рядка або чека |- |Податкова група |Податкова група ПРРО |- |Оплата |Тип оплати: готівка, картка, інше |}

json=payload,

k2_document = { !Дія в K2 ERP <syntaxhighlight lang="python">

logger = logging.getLogger(__name__)

Дія

7.; |-

Рекомендовано Інтегрувати K2 ERP через локальний Device Manager API }
},
Перетворює документ продажу K2 ERP у payload для Device Manager.; |-
ARM Linux Не підтримується - 1056 Відсутні офлайн-номери - Python integration service }

) -> None:

!супровід
9f1d9f9d-32fb-4d3d-bd71-6a1b2a7c5f7a
== Таймаути ==
|-
|<code>1091</code>
|Пристрій не знайдено
|Перевірити назву `device` у K2 ERP та Device Manager.; # Зберегти `tag`.; |}

 return client.execute(payload)
 }

8.; |-
| style="background:#d4edda; color:#155724; font-weight:bold;" |Обовʼязково
|Z-звіт має бути повʼязаний із конкретною зміною K2 ERP.; |}

{| class="wikitable"
== Типи завдань ==

 "type": 1,
=== Значення поля type ===
{| class="wikitable"
=== Етап 5.; Тест повтору з тим самим tag ===
 self.session.auth = (config.username, config.password)


Python integration service
 self.response = response
<syntaxhighlight lang="python">
!Правило
 payload.update(fiscal_data)

<syntaxhighlight lang="python">
=== Правило формування tag ===

!Значення
=== Крок 1.; Встановити Device Manager ===
 "name": "Круасан",
 job = self._queue.get()
 "code": line.get("sku") or line ["id"],
K2_MAIN_KASA
7.; користувач системи проводить документ продажу в K2 ERP.; |-
|`queue.py`
|Черга операцій по кожній касі.; Вчасно.Каса
{| class="wikitable"

 }
Файли встановлення доступні на сторінці пакетів Device Manager.; Якщо чек не знайдено  вирішує, чи повторювати відправку з тим самим tag.; ├── test_response_handling.py
{| class="wikitable"
</pre>

== Фіскалізація чека продажу ==

 "device": self.config.device,
3.; Повторити з тим самим tag.; DeviceManagerConfig(
== Обмеження паралельної роботи ==
print(result)
{| class="wikitable"
 "sum": round(float(payment ["amount"]), 2),
</pre>Або UUID:<pre>
print(response.text [:500])
!Файл

=== Крок 2.; Перевірити доступ до вебінтерфейсу ===

# Відправити два запити одночасно на одну касу.;<syntaxhighlight lang="python">
 }
import requests
== Закриття зміни / Z-звіт ==
def build_sale_payload(k2_document: dict) -> dict:
!res_action

=== Вебінтерфейси Device Manager ===
 if config.username and config.password:
</pre>де `{{dm_ip}}`  IP-адреса пристрою, на якому встановлено Device Manager.; Якщо чек не знайдено  повторити з тим самим tag або поставити в retry.; |-
| style="background:#f8d7da; color:#721c24; font-weight:bold;" |3
|Помилка
|Аналізувати `res` і `res_action`.; |-
|<code>1082</code>
|Неможливо зберегти інформаційні дані до БД
|Перевірити права запуску Device Manager або стан бази.; class FiscalQueue:
 level=logging.INFO,
{| class="wikitable"

 for payment in k2_document ["payments"]:
 tag = f"K2:SHIFT:{k2_shift_id}:OPEN"
</pre>Приклад:<pre>
curl http://192.168.1.50:3939
 "error": str(error) if error else None,
 "task": 1,
 },
 response.raise_for_status()
Для інтеграції K2 ERP з Вчасно.Каса рекомендовано використовувати Device Manager як локальний API-шлюз, а між K2 ERP та Device Manager створити окремий Python-сервіс.; |-
|<code>11</code>
|Z-звіт
|Закриття зміни.; |}

 "tag": tag,
 fiscal_timeout: int = 25
=== Встановлення залежностей ===
 task=0,
== Обробка res_action ==
 )
 "price": 75.00,
 operation_type: str,
except VchasnoKasaTimeoutError:
 11.2.; локальний інтеграційний застосунок.Каса забезпечується через '''Device Manager від Вчасно.Каса'''.; |-
|id
|UUID / bigint
|Внутрішній ID операції.; "number": "INV-000123",
 "comment": f"K2 ERP document {k2_document ['number']}",
 except Exception:
!Статус
def open_shift(client: VchasnoDeviceManagerClient, k2_shift_id: str) -> Dict [str, Any]:
 

=== Python-метод ===
 data = response.json()
== Локальне API Device Manager ==
== Логування в Python ==
<syntaxhighlight lang="python">
 self.session = requests.Session()
!Статус
!Реакція
import uuid
!Правило
 def __init__(self, response: Dict [str, Any]):

K2 ERP
== Транзакційність через tag ==

 base_url="http://localhost:3939",
 try:

import threading

Джерела

client: VchasnoDeviceManagerClient,
Правило

HTTP REST API

Код Статус
return self.execute(payload)
device: str,
7.2.; - стабільний tag має зберігатися в K2 ERP.; |-
Транзакційність Python-сервіс контролює `tag`, повтори, статуси та логування.; !Помилка

Fiscal Integration Module

device="K2_TEST_KASA",
"response": response_payload,

except VchasnoKasaBusinessError as exc:

task_status = data.get("task_status")
У Python-сервісі штучно поставити малий timeout:
try:
!Статус

!Призначення
 fiscal_data: Optional [Dict [str, Any]] = None,
!операційна дія
 ],

├── fiscal_service.py
)
 "cnt": float(line ["quantity"]),
 "task": task,
client = VchasnoDeviceManagerClient(config)
 print("Expected business response:", exc.response)
 істотно:
 )

 }
!Поле
 terminal_timeout: int = 310
== Python-клієнт для Device Manager ==
├── errors.py
!Коментар

 result = client.find_by_tag("TEST-NOT-EXISTING-TAG")
== Перевірка чека за tag ==
 "id": "ITEM-002",
 except VchasnoKasaDeviceBusyError:
import queue

 },
 ],
 payload: Dict [str, Any],
 "tax_group": 1,
=== Етап 2.; Перевірка доступу до API ===

 task: int,

# Відправити чек.; K2:SALE:INV-000123:FISCAL:20260506

 return client.fiscal_request(
pip install requests pydantic

6.; |-
| style="background:#d4edda; color:#155724; font-weight:bold;" |
|Робочу касу додано.; |-
|errortxt
|text
|Текст помилки.; Це означає:

Device Manager Вчасно.Каса

* два одночасні запити на одну касу виконуватись не будуть;
* перший запит буде виконуватись;
* наступний запит має змогу отримати помилку `1105`;
* для однієї каси у Python-сервісі потрібно зробити чергу.;
Якщо K2 ERP або Python-сервіс функціонує на іншому сервері:
<syntaxhighlight lang="python">
!Статус

import requests
 "device": "K2_TEST_KASA",

 if error:

 },

=== Базовий HTTP-клієнт ===
 8.2.; |-
| style="background:#f8d7da; color:#721c24; font-weight:bold;" |Не робити
|Не закривати зміну повторно з новим tag після timeout.; |-
| style="background:#d4edda; color:#155724; font-weight:bold;" ||Timeout для ПРРО не менше 20 секунд.;  def _run(self) -> None:
!K2 ERP
client = VchasnoDeviceManagerClient(
Офлайн-режим потрібно тестувати обережно та тільки на тестовій касі.; |-
| style="background:#f8d7da; color:#721c24; font-weight:bold;" |Небезпечно
|Вважати timeout фіскалізації невдалим без перевірки за `tag`.; |}

</pre>
class VchasnoKasaTimeoutError(VchasnoKasaError):
 super().__init__(message)

Тестування офлайн-режиму

1 fiscal - - Погано - Timeout під час переходу в офлайн - 1 Чек продажу - fiscal_number string None = None,
"sku": "482000000001",

from typing import Any, Dict, Optional

Призначення
Windows x32 / x64 - Принтер 6 секунд або більше - 1121 Дата на пристрої не співпадає з сервером - Обовʼязково class="wikitable" Рекомендована назва робочої каси:

== Типові помилки та реакція K2 ERP == </syntaxhighlight> ├── config.py

try:

class DeviceManagerConfig: == Початкове конфігурація == <pre>

message = response.get("errortxt") or "Vchasno.Kasa business error"
↓

├── logging_config.py == Рекомендована технічна архітектура для K2 ERP == !Тест 9.; !Статус import logging !URL

def find_by_tag(self, tag: str) -> Dict [str, Any]:
except requests.Timeout as exc:

!Пояснення == Черга операцій для однієї каси == 1.;== Встановлення Device Manager ==

result = client.find_by_tag(payload ["tag"])
raise VchasnoKasaBusinessError(data)

!Правило 3.; |- | style="background:#d4edda; color:#155724; font-weight:bold;" |Red Hat Linux |x86_64 |CentOS, Fedora.; |- |Відсутні офлайн-номери |Повертається помилка, операційна дія не вважається успішною.; Якщо чек знайдено — зберегти результат.; K2 ERP створює запис у fiscal_operations.; Якщо відповідь успішна:

{
"tag": tag,

=== Основні fiscal task ===

)
"amount": 175.00,

Python service

"payments": [

from __future__ import annotations def check_receipt_result(

"logged_at": datetime.utcnow().isoformat(),

1.; |}

{

K2:{document_type}:{document_id}:{operation_type}:{attempt_group} == Відкриття зміни == === Загальна схема === └── tests/ |- |<code>0</code> |Відкриття зміни |Початок роботи касира або торгової точки.; 8.3.; |- | style="background:#f8d7da; color:#721c24; font-weight:bold;" |3 |Помилка даних або стану |Не повторювати автономно.; |- | style="background:#f8d7da; color:#721c24; font-weight:bold;" |Не робити |Не відправляти паралельні запити на одну й ту саму касу |Device Manager поверне помилку `1105`: пристрій зайнятий.;=== Чому потрібен окремий Python-сервіс ===

"quantity": 2,

|- |Відсутність інтернету |Device Manager або ПРРО переходить у дозволений офлайн-режим, якщо це налаштовано.; |- | style="background:#f8d7da; color:#721c24; font-weight:bold;" |Неправильно |Відправляти всі чеки паралельно на одну касу.; ) -> Dict [str, Any]: http://192.168.1.50:3939/dm/vchasno-kasa/ ├── models.py

"type": 1,

=== Приклад використання === Мета інтеграції — дати K2 ERP можливість:

{
# Якщо не отримали відповідь, не створюємо новий чек.; |-

|res_action |int |Рекомендована дія за відповіддю.; |- |Банківський термінал |Оплата карткою, повернення, звірка, якщо підключено.; result = open_shift(client, "TEST-SHIFT-001")

self._queue: queue.Queue [Callable [[], None]] = queue.Queue()
response_payload: dict | None = None,

=== Production-рекомендація === === Призначення модулів === 2.; Виник timeout.; |- |updated_at |datetime |Дата актуалізація.; |- |Повернення онлайн |Офлайн-чеки мають бути синхронізовані.; return client.execute(payload, timeout=client.config.fiscal_timeout) 6.; |}

</syntaxhighlight> Device Manager REST API

Рекомендований алгоритм фіскалізації в K2 ERP

device Назва пристрою у Device Manager.; * Windows x32 та x64;
  • Debian-based Linux x86_64;
  • Red Hat-based Linux x86_64;
  • Arch Linux;
  • Android.; |-
4 Службова видача - Фіскалізація в офлайн class="wikitable"

Типові помилки офлайн-режиму

base_url="http://localhost:3939", ) 7.1.; |} Рекомендований формат:
</syntaxhighlight>

 ├── test_timeout.py

* не треба одразу вважати довший запит помилкою;
* timeout для ПРРО має бути не менше 20 секунд;
* відповідь Device Manager має змогу затриматись через спробу автоматичного переходу в офлайн;
* потрібно зберігати `tag` і повторювати перевірку результату;
* потрібно логувати, чи чек був фіскалізований онлайн або офлайн, якщо така відомості є собою у відповіді.; |-
device string - Debian Linux x86_64 Ubuntu, Debian, Mint.; tag = f"K2:SHIFT:{k2_shift_id}:ZREPORT" for line in k2_document ["lines"]: </syntaxhighlight> url = self._url("/dm/execute")

Логіка K2 ERP при timeout

pass
Перевірка print(result) config = DeviceManagerConfig( 8.1.; |- 1058 Чек з tag не знайдено - `fiscal_service.py` Бізнес-логіка фіскалізації, повторів, перевірки за tag.; └── test_fiscal_flow.py def _url(self, path: str) -> str: Ключові вимоги до інтеграції: payments = [] - назви вкладених полів fiscal/check/goods/payments потрібно звірити Тип

Мапінг документа K2 ERP у чек Вчасно.Каса

<syntaxhighlight lang="python">
Правильно Робити окрему FIFO-чергу на кожну касу.; # Перевірити, що вебсервер доступний на порту `3939`.; ↓ * характеристика API Device Manager * Файли для встановлення Device Manager