При работе с системой 1С:Предприятие 8.3 программисты часто сталкиваются с задачей программного создания новых элементов справочников. Одной из ключевых сложностей при этом является корректное присвоение уникального кода новому элементу. Этот код часто является основным бизнес-идентификатором, в отличие от системного уникального идентификатора (GUID). Если мы просто создадим новый объект справочника и вызовем метод Записать() без предварительного присвоения кода, система может выдать ошибку, особенно если для справочника установлена автонумерация или если код обязателен для заполнения. В этой статье мы подробно рассмотрим два основных подхода к решению этой задачи, а также обсудим важные аспекты обеспечения уникальности и обработки конкурентного доступа.
ПолучитьНовыйКод()Наиболее простой и часто используемый способ автоматического присвоения уникального кода новому элементу справочника — это использование метода ПолучитьНовыйКод(). Этот метод доступен у менеджера справочника и позволяет получить следующий свободный код в соответствии с настроенными правилами нумерации для данного справочника (например, автонумерация или нумерация по порядку).
Когда использовать: мы рекомендуем использовать этот подход, когда стандартная нумерация справочника нас полностью устраивает. Это может быть простая последовательная нумерация или нумерация с префиксами, если они настроены в конфигурации справочника.
Рассмотрим пошагово, как это сделать, на примере справочника "Номенклатура":
ПолучитьНовыйКод(). Этот метод возвращает строковое представление следующего доступного кода.Посмотрим на пример кода:
// Шаг 1: Получаем менеджер справочника "Номенклатура"
МенеджерСправочника = Справочники.Номенклатура;
// Шаг 2: Создаем новый элемент справочника
НовыйЭлементНоменклатуры = МенеджерСправочника.СоздатьЭлемент();
// Шаг 3: Получаем новый уникальный код
НовыйКод = МенеджерСправочника.ПолучитьНовыйКод();
// Шаг 4: Присваиваем полученный код новому элементу
НовыйЭлементНоменклатуры.Код = НовыйКод;
// Шаг 5: Заполняем другие необходимые реквизиты элемента, например, Наименование
НовыйЭлементНоменклатуры.Наименование = "Новый товар " + НовыйКод;
// Шаг 6: Записываем новый элемент
Попытка
НовыйЭлементНоменклатуры.Записать();
Сообщить("Элемент справочника '" + НовыйЭлементНоменклатуры.Наименование + "' успешно создан с кодом '" + НовыйЭлементНоменклатуры.Код + "'.");
Исключение Как Исключение
Сообщить("Ошибка при создании элемента справочника: " + Исключение.Описание);
КонецПопытки;
В этом примере, если в справочнике "Номенклатура" настроена простая числовая автонумерация, мы получим следующий по порядку числовой код. Если же настроен префикс (например, "ТВ-"), то код будет сформирован с учетом этого префикса (например, "ТВ-00000001"). Стоит отметить, что для пакетного изменения существующих кодов существует специальная обработка для добавления префикса. Важно понимать, что метод ПолучитьНовыйКод() учитывает текущие настройки справочника и гарантирует уникальность кода в рамках этих настроек.
Иногда стандартной автонумерации бывает недостаточно, и возникает необходимость сформировать код по более сложному, специфическому шаблону. Например, может потребоваться код вида "ПР-00001" для продукции, "УС-001" для услуг, или код, включающий дату или другие параметры. Похожие задачи возникают, когда требуется выполнить массовую перенумерацию справочников и документов или добавить префиксы к существующим. В таких случаях метод ПолучитьНовыйКод() уже не подходит, и мы должны реализовать логику формирования кода и проверки его уникальности вручную.
Когда использовать: этот подход мы выбираем, когда требуется гибкое, кастомизированное формирование кода, которое выходит за рамки стандартных возможностей автонумерации 1С.
Рассмотрим алгоритм действий:
НайтиПоКоду() менеджера справочника. Если элемент с таким кодом не найден (т.е., метод вернул пустую ссылку), значит, код уникален и его можно присваивать.Посмотрим на пример кода, который формирует код в формате "ПР-XXXXX" (где XXXXX — это пятизначный номер с ведущими нулями):
// Шаг 1: Определяем префикс для кода
ПрефиксКода = "ПР-";
// Шаг 2: Инициализируем начальный номер
Номер = 1;
// Шаг 3: Создаем новый элемент справочника "Номенклатура"
НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
// Шаг 4: В цикле формируем и проверяем уникальность кода
Пока Истина Цикл
// Формируем текущий вариант кода. Используем Формат() для добавления ведущих нулей.
// "ЧЦ=5" - число цифр 5, "ЧВ=0" - число десятичных знаков 0, "ЧН=0" - выводить ведущие нули
ТекущийКод = ПрефиксКода + Формат(Номер, "ЧЦ=5; ЧВ=0; ЧН=0;");
// Проверяем, существует ли элемент с таким кодом
// Если НайтиПоКоду() возвращает пустую ссылку, значит, код уникален
Если Справочники.Номенклатура.НайтиПоКоду(ТекущийКод).Пустая() Тогда
// Код уникален, присваиваем его новому элементу и выходим из цикла
НовыйЭлементНоменклатуры.Код = ТекущийКод;
Прервать;
КонецЕсли;
// Если код не уникален, увеличиваем номер и продолжаем цикл
Номер = Номер + 1;
// Предотвращение бесконечного цикла, если вдруг номера закончатся или возникнет логическая ошибка
Если Номер > 99999 Тогда // Максимальное количество для 5-значного номера
ВызватьИсключение "Не удалось сформировать уникальный код для номенклатуры. Закончились свободные номера.";
КонецЕсли;
КонецЦикла;
// Шаг 5: Заполняем другие необходимые реквизиты
НовыйЭлементНоменклатуры.Наименование = "Продукция " + НовыйЭлементНоменклатуры.Код;
// Шаг 6: Записываем новый элемент
Попытка
НовыйЭлементНоменклатуры.Записать();
Сообщить("Элемент справочника '" + НовыйЭлементНоменклатуры.Наименование + "' успешно создан с кодом '" + НовыйЭлементНоменклатуры.Код + "'.");
Исключение Как Исключение
Сообщить("Ошибка при создании элемента справочника: " + Исключение.Описание);
КонецПопытки;
В этом примере мы используем функцию Формат() для обеспечения требуемого количества знаков с ведущими нулями. Условие ЧН=0 гарантирует вывод ведущих нулей, если число меньше требуемой длины (ЧЦ=5). Ключевой момент здесь — цикл, который гарантирует, что мы найдем первый же свободный код, соответствующий нашему шаблону.
При программном создании элементов справочников, особенно в многопользовательских системах, таких как 1С:Предприятие, мы должны учитывать проблему конкурентного доступа. Представим ситуацию, когда несколько пользователей или длительные операции, выполняемые как фоновые задания, одновременно пытаются создать новый элемент справочника с автоматически присваиваемым уникальным кодом. В этом случае, если мы не предпримем специальных мер, может возникнуть одна из следующих проблем:
ПолучитьНовыйКод() или вручную сформируют один и тот же код, а затем попытаются записать элементы, система может выдать ошибку уникальности.Для предотвращения таких ситуаций мы можем использовать механизмы транзакций и блокировок 1С. Рассмотрим подробнее.
Транзакция — это последовательность операций, которая рассматривается как единое, неделимое целое. Либо все операции в транзакции выполняются успешно и фиксируются (коммит), либо при возникновении ошибки все изменения откатываются (откат). Оборачивание процесса создания элемента в транзакцию помогает гарантировать, что получение нового кода и его запись произойдут атомарно.
Посмотрим на пример использования транзакции:
// Начало транзакции
НачатьТранзакцию();
Попытка
// Здесь размещаем весь код по созданию элемента и присвоению уникального кода
// Например, любой из вариантов выше:
// Вариант 1: С использованием ПолучитьНовыйКод()
МенеджерСправочника = Справочники.Номенклатура;
НовыйЭлементНоменклатуры = МенеджерСправочника.СоздатьЭлемент();
// Этот вызов должен быть внутри транзакции для гарантированной атомарности
НовыйКод = МенеджерСправочника.ПолучитьНовыйКод();
НовыйЭлементНоменклатуры.Код = НовыйКод;
НовыйЭлементНоменклатуры.Наименование = "Новый товар " + НовыйКод;
НовыйЭлементНоменклатуры.Записать();
// Вариант 2: С формированием кода по шаблону (также внутри транзакции)
// ПрефиксКода = "ПР-";
// Номер = 1;
// НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
// Пока Истина Цикл
// ТекущийКод = ПрефиксКода + Формат(Номер, "ЧЦ=5; ЧВ=0; ЧН=0;");
// Если Справочники.Номенклатура.НайтиПоКоду(ТекущийКод).Пустая() Тогда
// НовыйЭлементНоменклатуры.Код = ТекущийКод;
// Прервать;
// КонецЕсли;
// Номер = Номер + 1;
// Если Номер > 99999 Тогда
// ВызватьИсключение "Не удалось сформировать уникальный код.";
// КонецЕсли;
// КонецЦикла;
// НовыйЭлементНоменклатуры.Наименование = "Продукция " + НовыйЭлементНоменклатуры.Код;
// НовыйЭлементНоменклатуры.Записать();
// Если все операции выполнены успешно, фиксируем транзакцию
ЗафиксироватьТранзакцию();
Сообщить("Элемент справочника успешно создан и зафиксирован в транзакции.");
Исключение Как Исключение
// В случае любой ошибки, отменяем все изменения внутри транзакции
ОтменитьТранзакцию();
Сообщить("Ошибка при создании элемента справочника (транзакция отменена): " + Исключение.Описание);
ВызватьИсключение; // Перевыбрасываем исключение, чтобы вышестоящий код мог его обработать
КонецПопытки;
Мы всегда рекомендуем оборачивать в транзакцию операции, где важна атомарность, особенно при работе с нумерацией и уникальными идентификаторами.
Для более тонкого контроля конкурентного доступа мы можем использовать объект БлокировкаДанных. Этот механизм позволяет программно установить эксклюзивную или разделяемую блокировку на данные в базе данных, предотвращая одновременное изменение одних и тех же данных разными сеансами. В контексте создания нового элемента справочника, явные блокировки могут быть полезны, когда мы хотим зарезервировать "право" на определенный диапазон кодов или на сам справочник на время генерации и записи кода.
Рассмотрим пример установки эксклюзивной блокировки на весь справочник "Номенклатура" на время создания элемента:
Блокировка = Новый БлокировкаДанных;
// Добавляем элемент блокировки для справочника "Номенклатура"
// Режим "Исключительный" означает, что никто другой не сможет записывать в этот справочник
ЭлементБлокировки = Блокировка.Добавить("Справочник.Номенклатура");
ЭлементБлокировки.УстановитьРежим(РежимБлокировкиДанных.Исключительный);
Попытка
// Устанавливаем блокировку. Это блокирует другие попытки записи.
Блокировка.Заблокировать();
// Здесь размещаем код по созданию элемента и присвоению уникального кода.
// Например, формирование кода по шаблону:
ПрефиксКода = "ПР-";
Номер = 1;
НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
Пока Истина Цикл
ТекущийКод = ПрефиксКода + Формат(Номер, "ЧЦ=5; ЧВ=0; ЧН=0;");
Если Справочники.Номенклатура.НайтиПоКоду(ТекущийКод).Пустая() Тогда
НовыйЭлементНоменклатуры.Код = ТекущийКод;
Прервать;
КонецЕсли;
Номер = Номер + 1;
Если Номер > 99999 Тогда
ВызватьИсключение "Не удалось сформировать уникальный код для номенклатуры. Закончились свободные номера.";
КонецЕсли;
КонецЦикла;
НовыйЭлементНоменклатуры.Наименование = "Продукция с блокировкой " + НовыйЭлементНоменклатуры.Код;
НовыйЭлементНоменклатуры.Записать();
Сообщить("Элемент справочника успешно создан с использованием блокировки.");
Исключение Как Исключение
Сообщить("Ошибка при создании элемента справочника с использованием блокировки: " + Исключение.Описание);
ВызватьИсключение;
КонецПопытки;
// Важно: Блокировка автоматически снимается по завершении вызова,
// но явный вызов Разблокировать() также возможен и иногда желателен
// Блокировка.Разблокировать();
Мы всегда рекомендуем быть осторожными с использованием эксклюзивных блокировок на весь справочник, так как они могут значительно снизить производительность системы в условиях высокой нагрузки. Чаще всего достаточно использования транзакций, особенно если логика получения нового кода реализована корректно. Однако в критических случаях, когда необходимо гарантировать абсолютную уникальность или предотвратить "гонки" за кодом, явные блокировки могут быть оправданы.
Мы подробно рассмотрели различные подходы к программному созданию элементов справочников в 1С:Предприятие 8.3 и присвоению им уникальных кодов. Мы выяснили, что для стандартной нумерации оптимальным решением будет использование метода ПолучитьНовыйКод(). В случаях, когда требуется формирование кода по индивидуальному шаблону, мы прибегаем к ручной генерации и проверке уникальности в цикле с использованием метода НайтиПоКоду().
Мы также проанализировали важность учета конкурентного доступа в многопользовательских средах. Мы узнали, что оборачивание операций в транзакцию является фундаментальной практикой для обеспечения атомарности и целостности данных при создании новых элементов. В некоторых специфических сценариях могут применяться явные блокировки данных для более строгого контроля.
Выбор конкретного метода зависит от требований к формированию кода и уровня конкуренции в системе. Мы всегда должны стремиться к написанию надежного и производительного кода, учитывающего все аспекты работы 1С:Предприятие. А для ускорения рутинных задач разработки можно использовать утилиты, позволяющие выполнять генерацию кода по ссылке на существующий объект, что помогает быстро получить шаблон для дальнейшей работы.