В современных складских и торговых системах штрихкодирование печатных форм документов является стандартом де-факто. Печать штрихкода на расходной накладной, заказе клиента или перемещении товаров позволяет мгновенно найти этот документ в базе данных при помощи обычного сканера штрихкодов. Однако при реализации этого механизма разработчики часто сталкиваются с технической сложностью: как закодировать уникальный идентификатор (GUID) документа в числовой штрихкод и, что еще важнее, как затем корректно декодировать считанный штрихкод обратно в полноценную ссылку на объект базы данных 1С?
В этой статье мы подробно разберем математическую суть прямого и обратного преобразования GUID в числовой код, рассмотрим готовые инструменты в типовых конфигурациях (на примере УТ 11, ERP и КА) и напишем собственный отказоустойчивый алгоритм поиска документов для самописных конфигураций.
Прежде чем приступать к обратному декодированию, нам необходимо четко понять, каким образом ссылка превращается в число на печатной форме. Ссылка на любой объект в 1С (будь то справочник или документ) физически опирается на 128-битное число — Уникальный Идентификатор (GUID). Стандартное строковое представление GUID выглядит как 36-символьная строка с дефисами, например: 58df113e-c7e3-4da8-7000-1e50ee4df678.
Для генерации компактного числового штрихкода (например, формата Code128) стандартные библиотеки 1С выполняют следующие действия:
Проанализируем фрагмент стандартного кода конфигурации, реализующий эту логику:
Функция ЧисловойКодПоСсылке(Ссылка) Экспорт
// Извлекаем GUID и удаляем дефисы, получая HEX-строку
ШестнадчатиричноеЧисло = СтрЗаменить(Строка(Ссылка.УникальныйИдентификатор()), "-", "");
// Преобразуем HEX в десятичное число
Возврат ПреобразоватьИзШестнадцатиричнойСистемыСчисленияВДесятичноеЧисло(ШестнадчатиричноеЧисло);
КонецФункции
В результате работы этой функции мы получаем длинную последовательность цифр (длиной до 38-39 символов), которая кодируется в штрихкод формата Code128. Использование стандартного EAN13 здесь невозможно, так как он вмещает всего 13 цифр, чего критически мало для сохранения уникальности 128-битного идентификатора GUID.
Когда оператор сканирует штрихкод с листа бумаги, в систему поступает исходная десятичная строка (например, d94c7b60c3e311e9baae309c2344e8cc в шестнадцатеричном эквиваленте). Задача разработчика — выполнить шаги в строго обратном порядке:
8-4-4-4-12.УникальныйИдентификатор из полученной строки.Выясним, как решить эту задачу наиболее эффективными способами.
Если вы работаете в конфигурации, созданной на базе современных типовых решений фирмы "1С" (например, "Управление торговлей 11", "Комплексная автоматизация 2", "1С:ERP" или "Управление нашей фирмой"), вам не нужно изобретать велосипед и писать сложные математические функции с нуля. В составе этих конфигураций присутствует специализированный программный модуль.
Для быстрого поиска документа по штрихкоду воспользуемся функцией общего модуля ШтрихкодированиеПечатныхФорм. Рассмотрим на примере, как правильно вызвать этот метод:
// Шаг 1. Определяем массив менеджеров документов, среди которых мы ожидаем найти объект.
// Настоятельно рекомендуется ограничивать этот список только нужными типами документов
// для оптимизации производительности запроса к СУБД.
МассивМенеджеров = Новый Массив;
МассивМенеджеров.Добавить(Документы.РеализацияТоваровУслуг);
МассивМенеджеров.Добавить(Документы.ЗаказКлиента);
МассивМенеджеров.Добавить(Документы.ПеремещениеТоваров);
// Шаг 2. Задаем считанный со сканера числовой штрихкод
СчитанныйШтрихкод = "28859182374921384021983472198347129843"; // Пример числовой строки
// Шаг 3. Вызываем встроенную функцию для получения массива ссылок
НайденныеСсылки = ШтрихкодированиеПечатныхФорм.ПолучитьСсылкуПоШтрихкодуТабличногоДокумента(СчитанныйШтрихкод, МассивМенеджеров);
// Шаг 4. Обрабатываем результат поиска
Если НайденныеСсылки.Количество() > 0 Тогда
СсылкаНаДокумент = НайденныеСсылки[0];
ПоказатьЗначение(, СсылкаНаДокумент);
Иначе
ВызватьИсключение "Документ по данному штрихкоду не найден в информационной базе!";
КонецЕсли;
Этот метод чрезвычайно удобен, безопасен и оптимален. Внутри себя он не просто выполняет математическое декодирование, но и автоматически строит быстрый запрос к базе данных СУБД с конструкцией ОБЪЕДИНИТЬ ВСЕ по выбранным таблицам, гарантируя, что полученная ссылка реально существует в базе данных и не является "битой".
Что делать, если ваша конфигурация написана с нуля или является сильно урезанной версией, где модуль ШтрихкодированиеПечатныхФорм отсутствует? В таком случае нам придется реализовать математику и логику поиска самостоятельно. Разберем этот процесс по шагам.
Поскольку 128-битный GUID при переводе в десятичную систему дает гигантские числа (до 39 знаков), стандартные типы данных 1С могут округлять значения или вызывать переполнение разрядной сетки (платформа поддерживает точность чисел только до 38 знаков). Чтобы избежать потери точности, мы должны использовать строковые алгоритмы деления "в столбик" (длинную арифметику).
Рассмотрим функцию, которая безошибочно преобразует сверхдлинную десятичную строку в шестнадцатеричную:
Функция ДесятичноеЧислоВШестнадцатеричное(Знач ДесятичноеСтрокой) Экспорт
Результат = "";
Алфавит = "0123456789abcdef";
Пока Истина Цикл
ДесятичноеСтрокой = СокрЛП(ДесятичноеСтрокой);
Если ДесятичноеСтрокой = "0" Или ДесятичноеСтрокой = "" Тогда
Прервать;
КонецЕсли;
Остаток = 0;
НоваяСтрокаЧисла = "";
// Посимвольное деление длинной строки на 16 (аналог деления "уголком")
Для Индекс = 1 По СтрДлина(ДесятичноеСтрокой) Цикл
ТекущаяЦифра = Число(Сред(ДесятичноеСтрокой, Индекс, 1));
ВременноеЧисло = Остаток * 10 + ТекущаяЦифра;
ЦелаяЧасть = Цел(ВременноеЧисло / 16);
Остаток = ВременноеЧисло % 16;
Если НоваяСтрокаЧисла <> "" Или ЦелаяЧасть > 0 Тогда
НоваяСтрокаЧисла = НоваяСтрокаЧисла + Строка(ЦелаяЧасть);
КонецЕсли;
КонецЦикла;
Результат = Сред(Алфавит, Остаток + 1, 1) + Результат;
ДесятичноеСтрокой = НоваяСтрокаЧисла;
КонецЦикла;
Если Результат = "" Тогда
Результат = "0";
КонецЕсли;
Возврат Результат;
КонецФункции
После получения HEX-строки нам необходимо дополнить ее ведущими нулями до длины в 32 символа и расставить дефисы. Посмотрим на пример реализации этой процедуры:
Функция ПолучитьGUIDИзЧисловогоШтрихкода(Знач ШтрихкодДесятичный) Экспорт
// Получаем HEX представление из десятичной строки
HEXСтрока = ДесятичноеЧислоВШестнадцатеричное(ШтрихкодДесятичный);
// Дополняем ведущими нулями до стандартного размера GUID (32 символа без дефисов)
Пока СтрДлина(HEXСтрока) < 32 Цикл
HEXСтрока = "0" + HEXСтрока;
КонецЦикла;
// Формируем строковое представление UUID по стандартной маске: 8-4-4-4-12
СтрокаGUID = Сред(HEXСтрока, 1, 8) + "-" +
Сред(HEXСтрока, 9, 4) + "-" +
Сред(HEXСтрока, 13, 4) + "-" +
Сред(HEXСтрока, 17, 4) + "-" +
Сред(HEXСтрока, 21, 12);
Возврат СтрокаGUID;
КонецФункции
Теперь, когда у нас есть строковое представление GUID, мы можем легко создать объект УникальныйИдентификатор. Но помните: метод ПолучитьСсылка() у менеджеров документов не проверяет физическое существование объекта в СУБД, он лишь конструирует объект ссылки в оперативной памяти. Если документа с таким идентификатором нет, ссылка окажется "битой".
Для надежного поиска напишем функцию, использующую запрос:
Функция НайтиДокументПоДекодированномуGUID(СтрокаGUID, МассивМенеджеров) Экспорт
УИД = Новый УникальныйИдентификатор(СтрокаGUID);
Запрос = Новый Запрос;
ТекстЗапроса = "";
// Динамически строим запрос по переданным таблицам документов
Для Каждого Менеджер Из МассивМенеджеров Цикл
ИмяТаблицы = Менеджер.ПустаяСсылка().Метаданные().ПолноеИмя();
Если ТекстЗапроса <> "" Тогда
ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса +
"ВЫБРАТЬ РАЗРЕШЕННЫЕ Ссылка КАК Ссылка ИЗ " + ИмяТаблицы + " ГДЕ Ссылка = &Ссылка";
СсылкаПоУИД = Менеджер.ПолучитьСсылка(УИД);
Запрос.УстановитьПараметр("Ссылка", СсылкаПоУИД);
КонецЦикла;
Если ТекстЗапроса = "" Тогда
Возврат Неопределено;
КонецЕсли;
Запрос.Текст = ТекстЗапроса;
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.Ссылка;
КонецЕсли;
Возврат Неопределено;
КонецФункции
При использовании математического декодирования GUID необходимо помнить о нескольких критически важных моментах:
ОБЪЕДИНИТЬ ВСЕ создаст колоссальную нагрузку на SQL-сервер, что приведет к задержкам при сканировании. Всегда ограничивайте список поиска только теми типами документов, которые реально могут иметь штрихкод на бумаге.РАЗРЕШЕННЫЕ в тексте нашего запроса. Если у текущего пользователя нет прав на чтение определенного типа документов, стандартный запрос без этого ключевого слова вызовет критическую ошибку прав доступа и аварийно завершит работу всей процедуры сканирования.Проанализируем альтернативный подход, который упоминался в Сообщении 9 на форуме. Вместо математического кодирования длинного 128-битного GUID в громоздкое десятичное число, мы можем пойти классическим путем создания связей через базу данных.
Для этого в системе создается независимый регистр сведений ШтрихкодыДокументов со следующей структурой:
Штрихкод (Строка, длина 13 или 11 символов). Сюда можно записывать короткие уникальные числовые коды или коды формата EAN13 с префиксами внутренних штучных кодов (например, начинающиеся на 20-29).ДокументСсылка (ЛюбаяСсылка.Документы). Ссылка на реальный физический документ в базе.При записи или проведении документа система программно генерирует короткий уникальный штрихкод, записывает его в данный регистр сведений и выводит его на печатную форму. При сканировании штрихкода поиск выполняется мгновенно и элегантно с помощью обычного запроса к индексу регистра сведений.
Преимущества альтернативного подхода: Штрихкод на бумаге получается существенно короче. Он отлично считывается даже дешевыми сканерами штрихкодов под любым углом и при плохом качестве печати. Кроме того, полностью отсутствует нагрузка на математические вычисления длинной арифметики.
Недостатки альтернативного подхода: Требуется выделение физического места в базе данных под хранение записей регистра. В распределенных информационных базах (РИБ) необходимо тщательно настраивать правила синхронизации диапазонов генерации коротких кодов во избежание коллизий (дублирования штрихкодов на разных узлах сети).
Выбор конкретного решения всегда остается за разработчиком, исходя из архитектуры конфигурации, качества используемого торгового оборудования и объемов документооборота компании.