Печать листа QR-кодов из 1С через API: внешняя обработка, запрос и PDF
Три куска кода для внешней обработки: запрос к номенклатуре, функция запроса к API и команда формы, плюс две строки запроса, которых нет в других языках: запрет метки BOM и защищенное соединение.
Список товаров или объектов учета уже лежит в базе 1С, и выгружать его в таблицу ради листа с QR-кодами не обязательно. Внешняя обработка берет строки запросом, отправляет их на API генератора одним POST-запросом и получает готовый PDF с сеткой кодов на A4. Код на встроенном языке приведен целиком.
Запрос, ответ, лимиты и ключ разобраны в статье API генерации QR-кодов с примерами на Python, Node.js и PowerShell. Эта статья только про 1С: код обработки, строки из справочника и два параметра запроса, которых в примерах на Python и Node.js нет.
Откуда взять строки
В примере строки берутся из справочника Номенклатура, у него есть код и наименование; имена сверьте в конфигураторе своей конфигурации. Код уходит в QR-код как часть ссылки на карточку товара на вашем сайте, наименование становится подписью под кодом. Адрес карточки задает поле url_template: на место {value} API подставляет часть строки до первого пробела, остаток строки остается подписью. В примере стоит https://example.com/catalog/{value}, замените его на адрес своего каталога.
Запрос берет не больше 1500 строк, потому что столько помещается в один запрос к API, а список целиком должен уложиться в 256 КБ: при ответе 413 уменьшите число в ПЕРВЫЕ. Подпись длиннее 100 символов API не примет, поэтому наименование обрезается функцией Лев. Отбор под свою задачу, например по виду номенклатуры или по остатку на складе, добавьте в условие запроса.
&НаСервереБезКонтекста
Функция СтрокиСписка()
Запрос = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1500
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
| И НЕ Номенклатура.ЭтоГруппа
|УПОРЯДОЧИТЬ ПО
| Код");
Строки = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Строки.Добавить(СокрЛП(Выборка.Код) + " " + Лев(Выборка.Наименование, 100));
КонецЦикла;
Возврат Строки;
КонецФункции
Запрос к API из обработки
Функция ниже собирает JSON, отправляет его на qqkod.ru и при ответе 200 пишет PDF в файл, а при любом другом ответе разбирает JSON с ошибкой. Она объявлена без контекста, поэтому одинаково вызывается с клиента и с сервера.
&НаКлиентеНаСервереБезКонтекста
Функция ЗапроситьЛистКодов(Строки, ИмяФайла)
Тело = Новый Структура;
Тело.Вставить("lines", Строки);
Тело.Вставить("url_template", "https://example.com/catalog/{value}");
Тело.Вставить("output", "pdf");
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Тело);
ТекстJSON = Запись.Закрыть();
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json");
Запрос = Новый HTTPЗапрос("/api/v1/sheets", Заголовки);
// метка BOM в начале тела дает ответ 400
Запрос.УстановитьТелоИзСтроки(ТекстJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Соединение = Новый HTTPСоединение("qqkod.ru", 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL());
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Результат = Новый Структура("КодСостояния", Ответ.КодСостояния);
Если Ответ.КодСостояния = 200 Тогда
Ответ.ПолучитьТелоКакДвоичныеДанные().Записать(ИмяФайла);
Результат.Вставить("Листов", ЗначениеЗаголовка(Ответ.Заголовки, "X-QQkod-Total-Sheets"));
Иначе
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Результат.Вставить("Ошибка", ПрочитатьJSON(Чтение, Истина));
Результат.Вставить("ПовторЧерез", ЗначениеЗаголовка(Ответ.Заголовки, "Retry-After"));
КонецЕсли;
Возврат Результат;
КонецФункции
// имена заголовков HTTP не различают регистр, поэтому ищем без учета регистра
&НаКлиентеНаСервереБезКонтекста
Функция ЗначениеЗаголовка(Заголовки, Имя)
Для Каждого Заголовок Из Заголовки Цикл
Если ВРег(Заголовок.Ключ) = ВРег(Имя) Тогда
Возврат Заголовок.Значение;
КонецЕсли;
КонецЦикла;
Возврат "";
КонецФункции
Две строки в этой функции отличают 1С от других языков. Первая это третий параметр УстановитьТелоИзСтроки: он запрещает метку порядка байтов (BOM), три байта перед первой скобкой JSON. С меткой в теле API отвечает 400 BAD_JSON, хотя сам текст JSON правильный. Вторая это объект ЗащищенноеСоединениеOpenSSL седьмым параметром конструктора соединения вместе с портом 443: адрес API начинается с https, а защищенное соединение в 1С задается этим объектом.
Заголовок Content-Type ставится явно, без него API ответит 415. Кириллица в подписях уходит в JSON так, как ее записала ЗаписьJSON: API принимает и буквы, и \u-коды.
Ответ и ошибки
Команда формы вызывает функции выше и печатает результат в окно сообщений. При 200 это путь к PDF и число листов из заголовка X-QQkod-Total-Sheets, при 429 число секунд до повтора из заголовка Retry-After, при остальных ошибках код, текст и каждая строка из fields отдельным сообщением, например «Строка №2: подпись длиннее 100 символов», если убрать из запроса обрезку Лев. Что значат ответы 422 и 429 и куда писать с request_id, разобрано в статье про API.
&НаКлиенте
Процедура НапечататьЛист(Команда)
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
Результат = ЗапроситьЛистКодов(СтрокиСписка(), ИмяФайла);
Если Результат.КодСостояния = 200 Тогда
Сообщить("Листов: " + Результат.Листов + ", файл " + ИмяФайла);
ИначеЕсли Результат.КодСостояния = 429 Тогда
Сообщить("Лимит запросов, повторите через " + Результат.ПовторЧерез + " с");
Иначе
Ошибка = Результат.Ошибка.Получить("error");
Сообщить(Строка(Результат.КодСостояния) + " " + Ошибка.Получить("code") + ": " + Ошибка.Получить("message"));
Поля = Ошибка.Получить("fields");
Если ТипЗнч(Поля) = Тип("Соответствие") Тогда
Для Каждого Поле Из Поля Цикл
Для Каждого Текст Из Поле.Значение Цикл
Сообщить(Поле.Ключ + ": " + Текст);
КонецЦикла;
КонецЦикла;
КонецЕсли;
Сообщить("request_id " + Результат.Ошибка.Получить("meta").Получить("request_id"));
КонецЕсли;
КонецПроцедуры
Где это запускать
Код кладется в модуль формы внешней обработки или расширения, конфигурацию менять не нужно. Запускается в тонком клиенте на платформе 8.3.9 и выше: параметр BOM и объекты для JSON есть с 8.3.6, а TLS 1.2 в защищенном соединении с 8.3.9. С версии 8.3.21 платформа считает синхронные вызовы HTTP на клиенте устаревшими, асинхронные в этой статье не разбираются. В веб-клиенте этот код не выполнится, потому что синхронных методов HTTPСоединение там нет. Код проверен интерпретатором OneScript против живого API, а не на платформе.
С сервера функция ЗапроситьЛистКодов работает так же, но PDF ляжет на диск сервера. Чтобы отдать его пользователю, файл передают через временное хранилище, а работу с временным файлом на сервере завершают в том же вызове, в котором его создали. В облаке 1С:Фреш обработка проходит аудит, а профиль безопасности регулирует доступ с сервера к адресам в интернете. В этом примере запрос идет с клиента, и PDF сразу у пользователя.
Когда хватит родной печати 1С
1С:Бухгалтерия 8 с версии 3.0.142 сама печатает инвентарные этикетки основных средств (ОС) со штрихкодами для терминала сбора данных (ТСД): команда «Инвентарные этикетки» в документах «Поступление ОС» и «Принятие к учету ОС», сервис «ОС и НМА, Сервис, Печать инвентарных этикеток». Если ОС ведутся в ней и обход идет с ТСД, печатайте этикетки там. QR-код со ссылкой на карточку эта печать не дает, за ним и нужен запрос к API.
Какой код выбрать под сканер и под телефон, разобрано в статье штрих-код или QR-код для инвентаризации, а другие сценарии с кодами из учетной системы собраны в базе знаний.
FAQ
Как напечатать больше 1500 кодов из 1С?
Разбейте выборку на части по 1500 строк и отправьте каждую отдельным запросом, на каждую часть придет свой PDF. Без ключа проходит 10 запросов в минуту, лимит на сутки и ключ описаны в статье про API.
Что печатать, если у товаров нет карточек на сайте?
QR-код с текстом: уберите из тела url_template, добавьте content_type со значением text и собирайте строку как «Код | Наименование», разделитель у текста вертикальная черта. Приложение-сканер покажет код товара строкой, карточку такой QR-код не откроет.
Можно ли получить из 1С не PDF, а SVG или PNG?
Да, той же функцией: поставьте в output значение svg, svg_files или png. Расширение в ПолучитьИмяВременногоФайла заранее известно только у pdf: один лист или один код приходит файлом своего формата, несколько приходят архивом zip, имена файлов есть в таблице «Ответ» статьи про API.
Почему под кодом видна только часть наименования?
При коде 2 см под ним помещается 12 знаков подписи. При 2,5 см помещается 15 знаков, остаток подписи обрезается. Поставьте в тело qr_size_cm побольше или оставьте в подписи только нужное на бирке, например код вместо наименования.
Следующий шаг
Создайте внешнюю обработку с одной командой, вставьте три куска в модуль формы и на время пробы замените в тексте запроса ПЕРВЫЕ 1500 на ПЕРВЫЕ 3. Запустите команду и откройте PDF из сообщения: под кодами должны стоять наименования первых трех позиций справочника по порядку кодов, а телефон по коду должен открыть адрес каталога с кодом номенклатуры на месте {value}. Если вместо пути пришел код ответа, текст рядом говорит, что поправить.
Все поля запроса к API генератора QR-кодов: размер кода, рамка, формат и ориентация листа задаются полями того же JSON.
Соберите свой лист с QR-кодами
Вставьте список ссылок или загрузите файл, а мы разложим коды с подписями по листам A4 и отдадим PDF, SVG или PNG.
Открыть генератор