name: 1c-exchange description: Обмен с 1С — приём каталога POST /api/catalog/1c-import под неизменную обработку заказчика, туннель Cloudflare с прибора, проверка через Postman и из 1С, мок рабочих данных на время теста. Использовать при правке import_1c.py, схем Import1C*, туннеля и при вопросах «1С не видит весы».
Обмен с 1С
Контракт диктует обработка
integrations/1c/original-Libra.bsl — внешняя обработка 1С 8.2, уже работает у
заказчика и не правится без явной просьбы владельца. Наш приёмник
backend/app/api/import_1c.py подстроен под неё, а не наоборот:
| Обработка шлёт | Приёмник |
| --- | --- |
| заголовок X-API-Key | основной способ; Authorization: Bearer оставлен для curl. Пробелы по краям токена срезаются — его копируют руками |
| article (числовой артикул) | это и есть plu: в схеме validation_alias=AliasChoices("plu","article"), поле необязательное |
| позиции без артикула / длиннее 5 цифр | не 422 на весь пакет, а пропуск с записью в errors[] (plu: 0, name) — 1С печатает их в своём логе |
| GET /health («Проверить связь») | явный маршрут в main.py вместе с /healthz; без него SPA-заглушка отвечала 200 при любом состоянии |
| in_stock всегда true | отбор «только в наличии» делает 1С; чего нет в пакете — гасится (active=0) |
| replace_images | снимок существующему товару кладётся только с ним; новым — всегда |
На время записи киоск закрыт экраном «Оновлення»: бэкенд шлёт live.notify
«catalog_busy» до записи и «catalog» после — в finally, поэтому сорвавшаяся
выгрузка не оставит прибор за экраном ожидания (плюс минутная страховка в киоске).
Без этого покупатель успевал нажать карточку посреди выгрузки и получить этикетку
со старой ценой. После снятия экрана киоск возвращается к началу каталога, но не
тогда, когда взвешенное ещё не забрали.
Полная синхронизация опасна при сбое выгрузки: пустой пакет и пакет без единого
пригодного артикула отклоняются (400) до того, как что-то погаснет. После приёма —
live.notify("catalog"). Поля настройки SMK_HTTP_Настройки и формат тела —
integrations/1c/README.md; менять контракт можно только с обеих сторон разом.
Как 1С достаёт до прибора
Сервер 1С доступен только по RDP, приборы — за NAT провайдера без белого IP
(проверено: WAN роутера — приватный 192.168.12.x, проброс снаружи не отвечает).
Поэтому прибор сам держит исходящий туннель Cloudflare (сервис tunnel в
deploy/docker/compose.yml, скилл deployment), а 1С ходит на имя
vesy-N.<домен>, SSL = Истина. Tailscale отпал: на сервер 1С клиента ставить
нельзя. Если 8.2 не договорится по TLS — SSL = Ложь через порт 80 работает
(Cloudflare принимает HTTP), но у домена должно быть выключено «Always Use HTTPS».
Постоянный адрес
Домен smk-retail.com в аккаунте Cloudflare владельца. Туннель vesy-dev
(ID ef35cc5d-…) с маршрутом vesy-dev.smk-retail.com → http://127.0.0.1:8000 —
это машина разработчика: cloudflared tunnel run --token <из backend/.env, CLOUDFLARE_TUNNEL_TOKEN>. Токен надстройка Claude в Chrome не отдаёт (и правильно) —
его копирует владелец руками. Снаружи проверено: /health 200 из Кипра и Германии,
/admin 401 без пароля и 200 с ним, приём 1С отвечает своим 401/400. HTTP без TLS
через туннель тоже проходит — запасной путь для 1С 8.2 без OpenSSL.
Проверка на машине разработчика
- Бэкенд слушает
0.0.0.0(tools/dev.pyтак и запускает); вbackend/.envзаданS2L_IMPORT_TOKEN. - Быстрый туннель без домена:
cloudflared tunnel --no-autoupdate --url http://127.0.0.1:8000печатает имя*.trycloudflare.com; живёт, пока идёт команда, меняется при каждом старте. Убедиться снаружи:curl https://<имя>/healthи, если есть сомнения в сети, check-host.net (TCP/DNS с чужих узлов). - Postman: вкладка Authorization → No Auth, заголовок
X-API-Keyво вкладке Headers. Первая ловушка теста: в Authorization стоял Bearer с другим значением — 401, хотя curl с тем же токеном проходил. - Пакет — ровно как у обработки (
article, неplu), плюс позиция без артикула и с шестизначным — проверитьerrors[]. Отрицательные: без ключа 401, пустой пакет 400. - Из 1С: сначала «Проверить связь» (то же поле
Сервер, без тела), потом «Заполнить» → «ВыгрузитьВВесы».Couldn't resolve host name— в полеСерверлишнийhttps://, путь или пробел, либо DNS на RDP-сервере не отдаётtrycloudflare.com(nslookupтам же). Именованный туннель со своим доменом такие фильтры не трогают.
Результат первого прогона 2026-09-15: 415 позиций, 290 со снимками, 4 группы — киоск перерисовался сам.
Мок рабочих данных
Выгрузка гасит всё, чего нет в пакете, поэтому демо-каталог на время теста
уезжает в backend/data/mock/ (гитигнор):
cd backend && .venv/Scripts/python tools/mock_data.py stash # база в мок
cd backend && .venv/Scripts/python tools/mock_data.py restore # вернуть
Снимки мок не трогает: присланные из 1С лежат в backend/data/photos/ и
перекрывают демо-набор из сборки по коду товара (скилл product-photos).
Оба — при остановленном бэкенде: SQLite занят, на Windows файл не переименовать.
S2L_SEED_DEMO=0 в backend/.env, иначе пустая база на старте снова засеется
демо. Ловушка Windows: после убийства родителя uvicorn --reload дочерние
воркеры (--multiprocessing-fork в командной строке) живут дальше и держат базу —
добивать их отдельно.
Проверять приём одной позицией через curl нельзя: это полная синхронизация, и такой запрос погасил 415 боевых товаров — восстанавливать пришлось повторной отправкой всего каталога из базы. Тестовую позицию слать только внутри полного пакета или на пустой базе.
Next.js App Router Expert
Development
A skill that turns Claude into a Next.js App Router expert.
README Generator
Development
Creates professional and comprehensive README.md files for your projects.
API Documentation Writer
Development
Generates comprehensive API documentation in OpenAPI/Swagger format.