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

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

При работе с системой 1С:Предприятие 8.3 программисты часто сталкиваются с задачей программного создания новых элементов справочников. Одной из ключевых сложностей при этом является корректное присвоение уникального кода новому элементу. Этот код часто является основным бизнес-идентификатором, в отличие от системного уникального идентификатора (GUID). Если мы просто создадим новый объект справочника и вызовем метод Записать() без предварительного присвоения кода, система может выдать ошибку, особенно если для справочника установлена автонумерация или если код обязателен для заполнения. В этой статье мы подробно рассмотрим два основных подхода к решению этой задачи, а также обсудим важные аспекты обеспечения уникальности и обработки конкурентного доступа.

Автоматическое присвоение уникального кода с помощью метода ПолучитьНовыйКод()

Наиболее простой и часто используемый способ автоматического присвоения уникального кода новому элементу справочника — это использование метода ПолучитьНовыйКод(). Этот метод доступен у менеджера справочника и позволяет получить следующий свободный код в соответствии с настроенными правилами нумерации для данного справочника (например, автонумерация или нумерация по порядку).

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

Рассмотрим пошагово, как это сделать, на примере справочника "Номенклатура":

  1. Получим менеджер справочника. Менеджер справочника предоставляет доступ к общим операциям с объектами этого типа, включая получение нового кода.
  2. Вызовем метод ПолучитьНовыйКод(). Этот метод возвращает строковое представление следующего доступного кода.
  3. Присвоим полученный код новому элементу.
  4. Запишем новый элемент справочника.

Посмотрим на пример кода:


// Шаг 1: Получаем менеджер справочника "Номенклатура"
МенеджерСправочника = Справочники.Номенклатура;

// Шаг 2: Создаем новый элемент справочника
НовыйЭлементНоменклатуры = МенеджерСправочника.СоздатьЭлемент();

// Шаг 3: Получаем новый уникальный код
НовыйКод = МенеджерСправочника.ПолучитьНовыйКод();

// Шаг 4: Присваиваем полученный код новому элементу
НовыйЭлементНоменклатуры.Код = НовыйКод;

// Шаг 5: Заполняем другие необходимые реквизиты элемента, например, Наименование
НовыйЭлементНоменклатуры.Наименование = "Новый товар " + НовыйКод;

// Шаг 6: Записываем новый элемент
Попытка
    НовыйЭлементНоменклатуры.Записать();
    Сообщить("Элемент справочника '" + НовыйЭлементНоменклатуры.Наименование + "' успешно создан с кодом '" + НовыйЭлементНоменклатуры.Код + "'.");
Исключение Как Исключение
    Сообщить("Ошибка при создании элемента справочника: " + Исключение.Описание);
КонецПопытки;

В этом примере, если в справочнике "Номенклатура" настроена простая числовая автонумерация, мы получим следующий по порядку числовой код. Если же настроен префикс (например, "ТВ-"), то код будет сформирован с учетом этого префикса (например, "ТВ-00000001"). Стоит отметить, что для пакетного изменения существующих кодов существует специальная обработка для добавления префикса. Важно понимать, что метод ПолучитьНовыйКод() учитывает текущие настройки справочника и гарантирует уникальность кода в рамках этих настроек.

Формирование уникального кода по заданному шаблону

Иногда стандартной автонумерации бывает недостаточно, и возникает необходимость сформировать код по более сложному, специфическому шаблону. Например, может потребоваться код вида "ПР-00001" для продукции, "УС-001" для услуг, или код, включающий дату или другие параметры. Похожие задачи возникают, когда требуется выполнить массовую перенумерацию справочников и документов или добавить префиксы к существующим. В таких случаях метод ПолучитьНовыйКод() уже не подходит, и мы должны реализовать логику формирования кода и проверки его уникальности вручную.

Когда использовать: этот подход мы выбираем, когда требуется гибкое, кастомизированное формирование кода, которое выходит за рамки стандартных возможностей автонумерации 1С.

Рассмотрим алгоритм действий:

  1. Определим шаблон кода. Это может быть префикс, количество знаков, разделители и т.д.
  2. Начнем с начального номера. Обычно это 1 или 001.
  3. В цикле будем формировать код, комбинируя префикс и текущий номер.
  4. Проверим уникальность сформированного кода. Для этого мы можем использовать метод НайтиПоКоду() менеджера справочника. Если элемент с таким кодом не найден (т.е., метод вернул пустую ссылку), значит, код уникален и его можно присваивать.
  5. Если код не уникален, увеличим номер и повторим процесс в цикле.

Посмотрим на пример кода, который формирует код в формате "ПР-XXXXX" (где XXXXX — это пятизначный номер с ведущими нулями):


// Шаг 1: Определяем префикс для кода
ПрефиксКода = "ПР-";

// Шаг 2: Инициализируем начальный номер
Номер = 1;

// Шаг 3: Создаем новый элемент справочника "Номенклатура"
НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();

// Шаг 4: В цикле формируем и проверяем уникальность кода
Пока Истина Цикл
    // Формируем текущий вариант кода. Используем Формат() для добавления ведущих нулей.
    // "ЧЦ=5" - число цифр 5, "ЧВ=0" - число десятичных знаков 0, "ЧН=0" - выводить ведущие нули
    ТекущийКод = ПрефиксКода + Формат(Номер, "ЧЦ=5; ЧВ=0; ЧН=0;"); 
    
    // Проверяем, существует ли элемент с таким кодом
    // Если НайтиПоКоду() возвращает пустую ссылку, значит, код уникален
    Если Справочники.Номенклатура.НайтиПоКоду(ТекущийКод).Пустая() Тогда
        // Код уникален, присваиваем его новому элементу и выходим из цикла
        НовыйЭлементНоменклатуры.Код = ТекущийКод;
        Прервать; 
    КонецЕсли;
    
    // Если код не уникален, увеличиваем номер и продолжаем цикл
    Номер = Номер + 1;
    
    // Предотвращение бесконечного цикла, если вдруг номера закончатся или возникнет логическая ошибка
    Если Номер > 99999 Тогда // Максимальное количество для 5-значного номера
        ВызватьИсключение "Не удалось сформировать уникальный код для номенклатуры. Закончились свободные номера.";
    КонецЕсли;
КонецЦикла;

// Шаг 5: Заполняем другие необходимые реквизиты
НовыйЭлементНоменклатуры.Наименование = "Продукция " + НовыйЭлементНоменклатуры.Код;

// Шаг 6: Записываем новый элемент
Попытка
    НовыйЭлементНоменклатуры.Записать();
    Сообщить("Элемент справочника '" + НовыйЭлементНоменклатуры.Наименование + "' успешно создан с кодом '" + НовыйЭлементНоменклатуры.Код + "'.");
Исключение Как Исключение
    Сообщить("Ошибка при создании элемента справочника: " + Исключение.Описание);
КонецПопытки;

В этом примере мы используем функцию Формат() для обеспечения требуемого количества знаков с ведущими нулями. Условие ЧН=0 гарантирует вывод ведущих нулей, если число меньше требуемой длины (ЧЦ=5). Ключевой момент здесь — цикл, который гарантирует, что мы найдем первый же свободный код, соответствующий нашему шаблону.

Учет конкурентного создания элементов и блокировки

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

  1. Дублирование кодов: если два процесса одновременно вызовут ПолучитьНовыйКод() или вручную сформируют один и тот же код, а затем попытаются записать элементы, система может выдать ошибку уникальности.
  2. Некорректная нумерация: из-за особенностей работы с транзакциями и кэшированием в базе данных, последовательность кодов может нарушиться, или один и тот же код может быть "получен" разными сеансами, приводя к ошибкам.

Для предотвращения таких ситуаций мы можем использовать механизмы транзакций и блокировок 1С. Рассмотрим подробнее.

Использование транзакций для обеспечения целостности

Транзакция — это последовательность операций, которая рассматривается как единое, неделимое целое. Либо все операции в транзакции выполняются успешно и фиксируются (коммит), либо при возникновении ошибки все изменения откатываются (откат). Оборачивание процесса создания элемента в транзакцию помогает гарантировать, что получение нового кода и его запись произойдут атомарно.

Посмотрим на пример использования транзакции:


// Начало транзакции
НачатьТранзакцию();

Попытка
    // Здесь размещаем весь код по созданию элемента и присвоению уникального кода
    // Например, любой из вариантов выше:
    
    // Вариант 1: С использованием ПолучитьНовыйКод()
    МенеджерСправочника = Справочники.Номенклатура;
    НовыйЭлементНоменклатуры = МенеджерСправочника.СоздатьЭлемент();
    // Этот вызов должен быть внутри транзакции для гарантированной атомарности
    НовыйКод = МенеджерСправочника.ПолучитьНовыйКод(); 
    НовыйЭлементНоменклатуры.Код = НовыйКод;
    НовыйЭлементНоменклатуры.Наименование = "Новый товар " + НовыйКод;
    НовыйЭлементНоменклатуры.Записать();
    
    // Вариант 2: С формированием кода по шаблону (также внутри транзакции)
    // ПрефиксКода = "ПР-";
    // Номер = 1;
    // НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
    // Пока Истина Цикл
    //     ТекущийКод = ПрефиксКода + Формат(Номер, "ЧЦ=5; ЧВ=0; ЧН=0;");
    //     Если Справочники.Номенклатура.НайтиПоКоду(ТекущийКод).Пустая() Тогда
    //         НовыйЭлементНоменклатуры.Код = ТекущийКод;
    //         Прервать;
    //     КонецЕсли;
    //     Номер = Номер + 1;
    //     Если Номер > 99999 Тогда 
    //         ВызватьИсключение "Не удалось сформировать уникальный код.";
    //     КонецЕсли;
    // КонецЦикла;
    // НовыйЭлементНоменклатуры.Наименование = "Продукция " + НовыйЭлементНоменклатуры.Код;
    // НовыйЭлементНоменклатуры.Записать();
    
    // Если все операции выполнены успешно, фиксируем транзакцию
    ЗафиксироватьТранзакцию();
    Сообщить("Элемент справочника успешно создан и зафиксирован в транзакции.");
Исключение Как Исключение
    // В случае любой ошибки, отменяем все изменения внутри транзакции
    ОтменитьТранзакцию();
    Сообщить("Ошибка при создании элемента справочника (транзакция отменена): " + Исключение.Описание);
    ВызватьИсключение; // Перевыбрасываем исключение, чтобы вышестоящий код мог его обработать
КонецПопытки;

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

Явные блокировки данных

Для более тонкого контроля конкурентного доступа мы можем использовать объект БлокировкаДанных. Этот механизм позволяет программно установить эксклюзивную или разделяемую блокировку на данные в базе данных, предотвращая одновременное изменение одних и тех же данных разными сеансами. В контексте создания нового элемента справочника, явные блокировки могут быть полезны, когда мы хотим зарезервировать "право" на определенный диапазон кодов или на сам справочник на время генерации и записи кода.

Рассмотрим пример установки эксклюзивной блокировки на весь справочник "Номенклатура" на время создания элемента:


Блокировка = Новый БлокировкаДанных;
// Добавляем элемент блокировки для справочника "Номенклатура"
// Режим "Исключительный" означает, что никто другой не сможет записывать в этот справочник
ЭлементБлокировки = Блокировка.Добавить("Справочник.Номенклатура");
ЭлементБлокировки.УстановитьРежим(РежимБлокировкиДанных.Исключительный);

Попытка
    // Устанавливаем блокировку. Это блокирует другие попытки записи.
    Блокировка.Заблокировать();
    
    // Здесь размещаем код по созданию элемента и присвоению уникального кода.
    // Например, формирование кода по шаблону:
    ПрефиксКода = "ПР-";
    Номер = 1;
    НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
    Пока Истина Цикл
        ТекущийКод = ПрефиксКода + Формат(Номер, "ЧЦ=5; ЧВ=0; ЧН=0;");
        Если Справочники.Номенклатура.НайтиПоКоду(ТекущийКод).Пустая() Тогда
            НовыйЭлементНоменклатуры.Код = ТекущийКод;
            Прервать;
        КонецЕсли;
        Номер = Номер + 1;
        Если Номер > 99999 Тогда 
            ВызватьИсключение "Не удалось сформировать уникальный код для номенклатуры. Закончились свободные номера.";
        КонецЕсли;
    КонецЦикла;
    НовыйЭлементНоменклатуры.Наименование = "Продукция с блокировкой " + НовыйЭлементНоменклатуры.Код;
    НовыйЭлементНоменклатуры.Записать();
    
    Сообщить("Элемент справочника успешно создан с использованием блокировки.");
Исключение Как Исключение
    Сообщить("Ошибка при создании элемента справочника с использованием блокировки: " + Исключение.Описание);
    ВызватьИсключение;
КонецПопытки;
// Важно: Блокировка автоматически снимается по завершении вызова, 
// но явный вызов Разблокировать() также возможен и иногда желателен
// Блокировка.Разблокировать(); 

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

Заключение

Мы подробно рассмотрели различные подходы к программному созданию элементов справочников в 1С:Предприятие 8.3 и присвоению им уникальных кодов. Мы выяснили, что для стандартной нумерации оптимальным решением будет использование метода ПолучитьНовыйКод(). В случаях, когда требуется формирование кода по индивидуальному шаблону, мы прибегаем к ручной генерации и проверке уникальности в цикле с использованием метода НайтиПоКоду().

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

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

← На главную