Как лучше организовать счетчик в документах под штрихкодирование в 1С?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Управленческий учет IT и автоматизация бизнеса
← На главную

Внедрение штрихкодирования печатных форм документов в «1С:Бухгалтерия 3» — это отличный способ ускорить обработку бумажного архива и минимизировать ошибки ручного ввода. Типичный сценарий выглядит так: на печатную форму документа выводится уникальный штрихкод. После того как бумажная копия возвращается с подписью контрагента, ответственный сотрудник сканирует этот штрихкод, и система мгновенно находит и открывает нужный документ в базе данных. При большом потоке документов программные роботы на платформе One RPA могут самостоятельно открывать найденные объекты и выполнять с ними нужные операции.

Однако при проектировании этой системы разработчики часто сталкиваются со сложными архитектурными вопросами: какой формат штрихкода выбрать, как избежать дублирования номеров при одновременной работе десятков пользователей и как организовать хранение штрихкодов в информационной базе. Разберем детально несколько эффективных подходов к решению этой задачи, проанализируем их плюсы и минусы, а также посмотрим на практические примеры реализации.

Вариант 1. Генерация штрихкода на лету на основе GUID (Рекомендуемый способ)

Этот подход активно применяется в современных типовых конфигурациях 1С (таких как «Управление торговлей 11» или «ERP»). Его суть заключается в том, что нам вообще не требуется создавать выделенные регистры сведений или справочники для хранения штрихкодов документов, а также решать проблему конкурентного доступа при записи счетчиков.

У каждого объекта (документа или справочника) в СУБД есть уникальный идентификатор — УникальныйИдентификатор (GUID). Он гарантированно уникален в пределах всей базы данных и даже за ее пределами. Мы можем преобразовать этот GUID в строку, закодировать его в штрихкод формата Code-128 или двухмерный DataMatrix (QR-код) и вывести на печать. Для практической реализации этой задачи в «1С:Бухгалтерии» подойдёт готовая обработка для генерации QR-кода в УПД.

Рассмотрим, как это работает на практике. При генерации печатной формы мы берем GUID документа, удаляем из него дефисы для компактности и передаем в генератор штрихкода:


Функция ПолучитьКодДокументаДляПечати(СсылкаНаДокумент) Экспорт
    // Получаем строку уникального идентификатора без дефисов
    СтрокаGUID = СтрЗаменить(Строка(СсылкаНаДокумент.УникальныйИдентификатор()), "-", "");
    Возврат СтрокаGUID;
КонецФункции

При сканировании такого штрихкода нам достаточно восстановить исходный формат GUID (вернуть дефисы на свои места) и быстро получить ссылку на документ по идентификатору без выполнения тяжелых поисковых запросов к базе данных:


Функция НайтиДокументПоШтрихкодуGUID(Знач ШтрихкодСтрока) Экспорт
    Если СтрДлина(ШтрихкодСтрока) <> 32 Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // Восстанавливаем дефисы в структуре GUID (8-4-4-4-12)
    ШаблонGUID = Сред(ШтрихкодСтрока, 1, 8) + "-" +
                 Сред(ШтрихкодСтрока, 9, 4) + "-" +
                 Сред(ШтрихкодСтрока, 13, 4) + "-" +
                 Сред(ШтрихкодСтрока, 17, 4) + "-" +
                 Сред(ШтрихкодСтрока, 21, 12);
                 
    Попытка
        Идентификатор = Новый УникальныйИдентификатор(ШаблонGUID);
        // Пример поиска для документа "Реализация товаров и услуг"
        СсылкаНаДокумент = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(Идентификатор);
        
        // Проверяем, существует ли реальный документ в базе данных
        Если ОбщегоНазначения.СсылкаСуществует(СсылкаНаДокумент) Тогда
            Возврат СсылкаНаДокумент;
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("Штрихкодирование", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
    КонецПопытки;
    
    Возврат Неопределено;
КонецФункции

Преимущества этого метода:

  1. Абсолютная уникальность. Вероятность генерации двух одинаковых GUID равна нулю.
  2. Отсутствие блокировок СУБД. Пользователи никак не пересекаются при печати разных документов. Нет транзакционных задержек.
  3. Простота поддержки в РИБ. При обмене данными между распределенными базами штрихкоды никогда не задвоятся.

Недостатки:

Вариант 2. Создание регистра сведений и использование формата EAN-13

Бывают ситуации, когда руководство предприятия жестко настаивает на использовании формата EAN-13 (например, если на предприятии уже закуплены простейшие одномерные сканеры, которые не умеют читать ничего, кроме стандартных «товарных» кодов). Поскольку EAN-13 содержит всего 12 значащих цифровых символов (13-й символ — это контрольное число), закодировать туда GUID физически невозможно. Нам придется вести сквозной счетчик и хранить соответствия в базе.

Для реализации этого подхода создадим независимый непериодический регистр сведений ШтрихкодыДокументов со следующей структурой:

При генерации номера штрихкода EAN-13 важно правильно структурировать эти 12 цифр, чтобы избежать коллизий. Проанализируем оптимальную структуру кода:

  1. Префикс внутреннего использования (2 символа). Международный стандарт выделяет для внутренних нужд предприятия коды, начинающиеся с цифр 20–29. Будем использовать, например, префикс 24.
  2. Префикс узла распределенной базы (1-2 символа). Если у вас используется РИБ, этот префикс жизненно необходим, чтобы пользователи в филиалах не сгенерировали одинаковые штрихкоды. Выделим под него, например, 3-й символ штрихкода. Для регулярной синхронизации данных между узлами в таком сценарии настраивается автоматический обмен данными между базами 1С по заданному расписанию.
  3. Код типа документа (2 символа). Позволит системе быстро определить, в какой таблице искать документ (например, 01 — Реализация, 02 — Счет-фактура, 03 — Акт).
  4. Счетчик (6–7 символов). Порядковый номер штрихкода.
  5. Контрольный символ (1 символ). Рассчитывается по стандартному алгоритму EAN-13.

Выясним причину главной проблемы этого метода: если два пользователя одновременно нажмут кнопку печати документов, система попытается прочитать последний номер из регистра, увеличить его на единицу и записать обратно. Без использования механизмов контроля транзакций это неизбежно приведет либо к ошибке блокировки записи, либо к выдаче одинаковых штрихкодов двум разным документам.

Вариант 3. Решение проблемы конкурентного доступа с помощью управляемых блокировок

Чтобы исключить дублирование номеров при генерации штрихкодов в регистре сведений, нам необходимо использовать механизм управляемых блокировок. Мы должны гарантировать, что в момент получения максимального номера штрихкода из регистра и до момента записи новой строки, ни один другой сеанс не сможет прочитать эти же данные или внести изменения.

Рассмотрим пример функции безопасного получения нового штрихкода с установкой исключительной управляемой блокировки регистра:


Функция ПолучитьНовыйШтрихкодДокумента(СсылкаНаДокумент, ПрефиксУзла = "0", КодТипаДокумента = "01") Экспорт
    
    НачатьТранзакцию();
    
    Попытка
        // Устанавливаем управляемую блокировку на регистр штрихкодов
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ШтрихкодыДокументов");
        ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
        Блокировка.Заблокировать();
        
        // Ищем в регистре штрихкод, который уже привязан к этому документу
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   Штрихкоды.Штрихкод КАК Штрихкод
            |ИЗ
            |   РегистрСведений.ШтрихкодыДокументов КАК Штрихкоды
            |ГДЕ
            |   Штрихкоды.ДокументСсылка = &СсылкаНаДокумент";
            
        Запрос.УстановитьПараметр("СсылкаНаДокумент", СсылкаНаДокумент);
        РезультатЗапроса = Запрос.Выполнить();
        
        Если Не РезультатЗапроса.Пустой() Тогда
            Выборка = РезультатЗапроса.Выбрать();
            Выборка.Следующий();
            ЗафиксироватьТранзакцию();
            Возврат Выборка.Штрихкод;
        КонецЕсли;
        
        // Если кода еще нет, получаем максимальный порядковый номер для нашей структуры
        ЗапросМаксКод = Новый Запрос;
        ЗапросМаксКод.Текст = 
            "ВЫБРАТЬ
            |   МАКСИМУМ(ПОДСТРОКА(Штрихкоды.Штрихкод, 6, 7)) КАК МаксНомер
            |ИЗ
            |   РегистрСведений.ШтрихкодыДокументов КАК Штрихкоды
            |ГДЕ
            |   Штрихкоды.Штрихкод ПОДОБНО &МаскаШтрихкода";
            
        // Маска для поиска: ПрефиксИспользования(24) + ПрефиксУзла + КодТипаДокумента
        Маска = "24" + ПрефиксУзла + КодТипаДокумента + "%";
        ЗапросМаксКод.УстановитьПараметр("МаскаШтрихкода", Маска);
        
        РезультатМакс = ЗапросМаксКод.Выполнить();
        НовыйНомер = 1;
        
        Если Не РезультатМакс.Пустой() Тогда
            ВыборкаМакс = РезультатМакс.Выбрать();
            ВыборкаМакс.Следующий();
            Если ЗначениеЗаполнено(ВыборкаМакс.МаксНомер) Тогда
                НовыйНомер = Число(ВыборкаМакс.МаксНомер) + 1;
            КонецЕсли;
        КонецЕсли;
        
        // Форматируем номер до 7 символов (дополняем нулями слева)
        СтрокаНомера = Формат(НовыйНомер, "ЧЦ=7; ЧДЦ=0; ЧГ=0");
        СтрокаНомера = СтрЗаменить(СтрокаНомера, " ", "0");
        
        // Собираем 12 цифр без контрольного символа
        ШтрихкодБезКонтрольного = "24" + ПрефиксУзла + КодТипаДокумента + СтрокаНомера;
        
        // Рассчитываем контрольный символ по алгоритму EAN-13
        КонтрольныйСимвол = РассчитатьКонтрольныйСимволEAN13(ШтрихкодБезКонтрольного);
        ИтоговыйШтрихкод = ШтрихкодБезКонтрольного + Строка(КонтрольныйСимвол);
        
        // Записываем новый штрихкод в регистр
        МенеджерЗаписи = РегистрыСведений.ШтрихкодыДокументов.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Штрихкод = ИтоговыйШтрихкод;
        МенеджерЗаписи.ДокументСсылка = СсылкаНаДокумент;
        МенеджерЗаписи.Записать(Истина);
        
        ЗафиксироватьТранзакцию();
        Возврат ИтоговыйШтрихкод;
        
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение "Не удалось сгенерировать уникальный штрихкод документа: " + КраткоеОписаниеОшибки(ОписаниеОшибки());
    КонецПопытки;
    
КонецФункции

Функция РассчитатьКонтрольныйСимволEAN13(Штрихкод12)
    Четные = 0;
    Нечетные = 0;
    Для Индекс = 1 По 12 Цикл
        Цифра = Число(Сред(Штрихкод12, Индекс, 1));
        Если Индекс % 2 = 0 Тогда
            Четные = Четные + Цифра;
        Иначе
            Нечетные = Нечетные + Цифра;
        КонецЕсли;
    КонецЦикла;
    Сумма = Нечетные + (Четные * 3);
    Остаток = Сумма % 10;
    Если Остаток = 0 Тогда
        Возврат 0;
    Иначе
        Возврат 10 - Остаток;
    КонецЕсли;
КонецФункции

Проанализируем этот код подробнее. Использование управляемой блокировки на уровне СУБД гарантирует, что параллельный сеанс, вызвавший эту же функцию, приостановит свое выполнение на строке Блокировка.Заблокировать() и будет ожидать завершения транзакции первого пользователя (пока не выполнится ЗафиксироватьТранзакцию() или ОтменитьТранзакцию()). Это полностью исключает генерацию дубликатов штрихкодов в условиях высокой конкурентной нагрузки.

Вариант 4. Формирование штрихкода формата Code-128 по структуре «Префикс + Номер + Дата»

Еще один популярный вариант, который часто применяется на практике, — кодирование в штрихкод Code-128 понятной человеку текстовой строки. Например, структуры вида: КодОрганизации + КодТипаДокумента + НомерДокумента + ДатаДокумента.

Допустим, документ «Реализация товаров и услуг» № РУ00-00142 от 15 октября 2023 года организации с кодом «МС» можно закодировать строкой: МС-РТУ-00142-15102023.

Рассмотрим особенности этого решения:

Использование стандартной компоненты БСП для генерации штрихкода

Разработчикам современных конфигураций на платформе «1С:Предприятие 8.3» больше нет необходимости устанавливать на компьютеры пользователей устаревшие внешние шрифты или сторонние компоненты ActiveX. В составе Библиотеки стандартных подсистем (БСП) поставляется Native-компонента печати штрихкодов, которая отлично работает как на стороне Windows-клиентов, так и под управлением серверов 1С на ОС Linux. Для удобной доработки печатных форм БСП можно использовать метод постобработки печатных форм с помощью расширения.

Проанализируем пример кода, выводящего сгенерированный штрихкод в область табличного документа печатной формы с использованием механизмов БСП:


Процедура ВывестиШтрихкодВТабличныйДокумент(ТабличныйДокумент, ОбластьМакета, ЗначениеШтрихкода)
    
    // Параметры для формирования штрихкода компонентой БСП
    ПараметрыШтрихкода = Новый Структура;
    ПараметрыШтрихкода.Вставить("Ширина", 400);
    ПараметрыШтрихкода.Вставить("Высота", 150);
    ПараметрыШтрихкода.Вставить("ТипКода", 1); // 1 - это тип EAN13. Для Code128 используется значение 4.
    ПараметрыШтрихкода.Вставить("ОтображатьТекст", Истина);
    ПараметрыШтрихкода.Вставить("РазмерШрифта", 10);
    ПараметрыШтрихкода.Вставить("УголПоворота", 0);
    ПараметрыШтрихкода.Вставить("Штрихкод", ЗначениеШтрихкода);
    
    // Получаем двоичные данные картинки штрихкода через БСП
    КартинкаДвоичныеДанные = МенеджерОборудованияВызовСервера.ПолучитьКартинкуШтрихкода(ПараметрыШтрихкода);
    
    Если КартинкаДвоичныеДанные <> Неопределено Тогда
        // Создаем объект Картинка и помещаем его на макет в область рисунка
        КартинкаШтрихкода = Новый Картинка(КартинкаДвоичныеДанные);
        РисунокМакета = ОбластьМакета.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
        РисунокМакета.Картинка = КартинкаШтрихкода;
        
        // Позиционируем рисунок в созданной области макета
        РисунокМакета.Расположить(ОбластьМакета.Область("R1C1:R3C5"));
        РисунокМакета.ГраницаСверху = Ложь;
        РисунокМакета.ГраницаСнизу = Ложь;
        РисунокМакета.ГраницаСлева = Ложь;
        РисунокМакета.ГраницаСправа = Ложь;
    КонецЕсли;
    
КонецПроцедуры

Подведем итоги и выберем оптимальное решение

Мы проанализировали различные варианты организации штрихкодирования документов в 1С. Выбор конкретного подхода зависит от требований вашего бизнеса и инфраструктуры:

← На главную