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