Как найти наименьший свободный код справочника в заданном диапазоне в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

При переносе данных между системами, интеграции с торговым оборудованием (весами с печатью этикеток, кассовыми узлами) или штрихкодировании часто возникает необходимость присваивать элементам справочника коды из строго определенного диапазона (например, от 00000000001 до 00000000999). Штатный автонумератор платформы 1С всегда выбирает максимальный существующий код и увеличивает его на единицу, поэтому он не умеет заполнять образовавшиеся «дыры» и пропуски в нумерации.

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

Алгоритм последовательного поиска первого свободного номера выборкой

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

Проанализируем практический пример функции, возвращающей минимальный свободный код полной длины (11 знаков):


Функция ПолучитьНовыйКод(ГраницаДиапазона = 999, ДлинаКода = 11) Экспорт
    
    // Формируем строковое значение верхней границы с ведущими нулями
    СтрокаГраницы = Формат(ГраницаДиапазона, "ЧЦ=" + ДлинаКода + "; ЧРГ=; ЧГ=0");
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    Номенклатура.Код КАК Код
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Код <= &ГраницаДиапазона
    |    И НЕ Номенклатура.ЭтоГруппа
    |УПОРЯДОЧИТЬ ПО
    |    Номенклатура.Код ВОЗР";
    
    Запрос.УстановитьПараметр("ГраницаДиапазона", СтрокаГраницы);
    
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    
    ТекЧислоКода = 0;
    
    Пока Выборка.Следующий() Цикл
        // Приводим строковый код из базы к числу для арифметического сравнения
        ЧислоИзБазы = Число(Выборка.Код);
        
        Если (ЧислоИзБазы - ТекЧислоКода) > 1 Тогда
            // Найден пропуск между предыдущим и текущим значением
            СвободноеЧисло = ТекЧислоКода + 1;
            Возврат Формат(СвободноеЧисло, "ЧЦ=" + ДлинаКода + "; ЧРГ=; ЧГ=0");
        КонецЕсли;
        
        ТекЧислоКода = ЧислоИзБазы;
    КонецЦикла;
    
    // Если пропусков в середине не было, проверяем следующий за последним
    Если ТекЧислоКода < ГраницаДиапазона Тогда
        СвободноеЧисло = ТекЧислоКода + 1;
        Возврат Формат(СвободноеЧисло, "ЧЦ=" + ДлинаКода + "; ЧРГ=; ЧГ=0");
    КонецЕсли;
    
    // Свободных кодов в диапазоне не осталось
    Возврат Неопределено;
    
КонецФункции

Особенности работы со строковыми кодами в запросах

Разберем важные нюансы поведения платформы 1С при работе со строковыми полями:

  1. Лексикографическое сравнение строк. В СУБД сравнение строковых кодов происходит посимвольно слева направо. Если передать в параметр строку "999" без лидирующих нулей, то в условие Код <= &ГраницаДиапазона попадут все коды, начинающиеся на цифры от "0" до "8" любой длины (например, "17000000000" или "55555555555"). Граничный параметр запроса обязательно должен содержать ровно столько же знаков, сколько составляет длина реквизита в метаданных.
  2. Форматирование чисел. Вместо конкатенации строк с подстрокой нулей рекомендуется использовать функцию Формат() с параметрами ЧЦ (число цифр) и ЧГ=0 (отключение группировки разрядов), что исключает случайное появление неразрывных пробелов в коде.

Точечный поиск через метод НайтиПоКоду

Если требуется разово найти свободный код для небольшого диапазона (например, до 1000 элементов) без выполнения запроса, разберем альтернативный вариант с перебором и методом НайтиПоКоду():


Функция НайтиСвободныйКодПеребором(МаксЗначение = 999, ДлинаКода = 11) Экспорт

    Для Счетчик = 1 По МаксЗначение Цикл
        СтрокаКода = Формат(Счетчик, "ЧЦ=" + ДлинаКода + "; ЧРГ=; ЧГ=0");
        
        СсылкаНаЭлемент = Справочники.Номенклатура.НайтиПоКоду(СтрокаКода);
        Если СсылкаНаЭлемент.Пустая() Тогда
            Возврат СтрокаКода;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Неопределено;

КонецФункции

Такой способ подходит для разовых обработок переноса или группового переназначения реквизитов, однако для высоконагруженных систем предпочтителен запрос к базе данных.

Правильное встраивание в логику конфигурации

Выясним, где именно в кодовой базе следует вызывать переопределение номера:

  1. Обработчик ПриУстановкеНовогоКода. В модуле объекта справочника Номенклатура перехватываем штатное назначение кода. Устанавливаем СтандартнаяОбработка = Ложь только для целевых условий (например, если родитель элемента входит в группу весового товара), после чего заполняем реквизит Код полученным значением.
  2. Блокировки и параллельная работа. При одновременной записи элементов несколькими пользователями выборка может вернуть один и тот же минимальный свободный код, что приведет к ошибке контроля уникальности. В транзакции записи рекомендуется устанавливать управляемую блокировку (объект БлокировкаДанных) по пространству кодов справочника.
  3. Разделение служебных кодов и PLU. Использование системного реквизита Код в качестве кассового номера (PLU) связывает бизнес-логику с технической нумерацией 1С. Архитектурно более надежным решением является выделение отдельного числового реквизита или регистра сведений для хранения кодов торгового оборудования.
← На главную