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 боевых товаров — восстанавливать пришлось повторной отправкой всего каталога из базы. Тестовую позицию слать только внутри полного пакета или на пустой базе.
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.