При работе в 1С:Управление нашей фирмой (УНФ) часто возникает потребность в расширении стандартного функционала путем создания дополнительных реквизитов. В нашем случае, пользователю требуется создать дополнительный реквизит строкового типа с именем "Код для сайта", который должен автоматически генерировать последовательный номер формата "r000001", "r000002" и так далее. Мы выясним, что типовой функционал 1С не предоставляет такого механизма для дополнительных реквизитов, но задача решаема с помощью расширений конфигурации. В этом руководстве мы рассмотрим два основных подхода к решению этой задачи, проанализируем их преимущества и недостатки, а также предоставим примеры кода.
Мы предлагаем рассмотреть два основных подхода к реализации автоматической генерации последовательного кода для дополнительного реквизита:
Этот подход предполагает перехват события создания или записи элемента, для которого был создан дополнительный реквизит, и программное заполнение этого реквизита следующим по порядку номером. Давайте разберем этот процесс по шагам.
Перед тем как приступить к написанию кода, необходимо создать сам дополнительный реквизит "Код для сайта" в режиме "Предприятие" через раздел Настройки -> Общие настройки -> Дополнительные реквизиты и сведения. Убедитесь, что он имеет тип "Строка" и привязан к нужному объекту (например, "Справочник.Номенклатура").
Для реализации нашей задачи без изменения типовой конфигурации мы будем использовать расширение. Если у вас еще нет расширения, создайте его в конфигураторе:
Конфигуратор.Конфигурация -> Расширения конфигурации.Добавить, укажите имя расширения и, при необходимости, другие параметры.Наиболее подходящим местом для генерации кода для *нового* элемента является событие ПослеЗаписиНаСервере основного объекта (например, справочника "Номенклатура"). Почему именно ПослеЗаписиНаСервере? Потому что на момент выполнения этого события объект уже имеет свою ссылку (Объект.Ссылка), что критически важно для работы с регистром дополнительных сведений.
Нам потребуется создать подписку на событие ПослеЗаписи для того объекта, для которого создан дополнительный реквизит. Например, если реквизит создан для справочника Номенклатура, то подписываемся на его событие.
ПодпискаНаСобытие.УстановкаКодаСайтаДляНоменклатуры.Источник выберите Справочник.Номенклатура (или другой объект, для которого вы создали реквизит).Событие выберите ПослеЗаписи.Обработчик укажите имя нового общего модуля, например, УстановкаДопРеквизитовСервер, и имя процедуры в нем, например, ПриПослеЗаписиНоменклатуры.Теперь мы перейдем к написанию кода в созданном общем модуле. Нам понадобятся две основные функции:
Функция для получения следующего кода:
Эта функция будет выполнять запрос к регистру сведений ЗначенияСвойствОбъектов, чтобы найти все ранее сгенерированные коды для сайта, извлечь из них числовую часть, определить максимальное значение и сформировать следующий код в нужном формате ("r000001").
// В общем модуле расширения (например, УстановкаДопРеквизитовСервер)
// Этот модуль должен быть с галками "Сервер", "ВызовСерверногоМетода"
&НаСервере
Функция ПолучитьСледующийКодДляСайта(МетаданныеОбъекта)
МаксимальныйНомер = 0;
// Шаг 1: Найдем ссылку на элемент плана видов характеристик по имени "КодДляСайта"
// Это необходимо, чтобы точно идентифицировать дополнительный реквизит в регистре.
ЗапросСвойства = Новый Запрос;
ЗапросСвойства.Текст =
"ВЫБРАТЬ
| ДополнительныеРеквизитыИСведения.Ссылка
|ИЗ
| ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения КАК ДополнительныеРеквизитыИСведения
|ГДЕ
| ДополнительныеРеквизитыИСведения.Имя = &ИмяСвойства";
ЗапросСвойства.УстановитьПараметр("ИмяСвойства", "КодДляСайта");
РезультатСвойства = ЗапросСвойства.Выполнить().Получить();
Если РезультатСвойства = Неопределено Тогда
// Если дополнительный реквизит с таким именем не найден, возвращаем начальный код.
// В реальной системе здесь можно было бы сгенерировать ошибку или записать в журнал.
Возврат "r000001";
КонецЕсли;
СсылкаНаСвойство = РезультатСвойства.Ссылка;
// Шаг 2: Выполним запрос к регистру ЗначенияСвойствОбъектов
// для поиска всех значений нашего дополнительного реквизита.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗначенияСвойствОбъектов.Значение КАК ЗначениеКода
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
| ЗначенияСвойствОбъектов.Свойство = &СсылкаНаСвойство
| И ЗначенияСвойствОбъектов.Объект.Ссылка В
| (ВЫБРАТЬ РАЗЛИЧНЫЕ
| ВТ_Объекты.Ссылка
| ИЗ
| &ИмяТаблицыОбъекта КАК ВТ_Объекты)"; // Динамический выбор таблицы объекта
Запрос.УстановитьПараметр("СсылкаНаСвойство", СсылкаНаСвойство);
// Для фильтрации по типу объекта (например, Справочник.Номенклатура)
Запрос.УстановитьПараметр("ИмяТаблицыОбъекта", МетаданныеОбъекта.ПолноеИмя());
Результат = Запрос.Выполнить().Выгрузить();
// Шаг 3: Проанализируем полученные значения и найдем максимальный номер
Для Каждого СтрокаТЗ Из Результат Цикл
КодСайта = Строка(СтрокаТЗ.ЗначениеКода);
// Проверяем, что код начинается с "r" и соответствует формату
Если Лев(КодСайта, 1) = "r" И СтрДлина(КодСайта) > 1 Тогда
Попытка
// Извлекаем числовую часть кода (например, из "r000001" получаем 1)
Номер = Число(Сред(КодСайта, 2));
Если Номер > МаксимальныйНомер Тогда
МаксимальныйНомер = Номер;
КонецЕсли;
Исключение
// Игнорируем значения, которые не удается преобразовать в число.
// Это позволяет избежать ошибок, если в поле присутствуют данные, не соответствующие формату.
КонецПопытки;
КонецЕсли;
КонецЦикла;
// Шаг 4: Формируем новый код с увеличенным на 1 номером
// Функция Формат позволяет дополнить число нулями до нужной длины (в данном случае 6 знаков).
Возврат "r" + Формат(МаксимальныйНомер + 1, "ЧЦ=7; ЧН=; ЧВН=; ЧФ=0");
КонецФункции
Процедура-обработчик подписки:
Эта процедура будет выполняться после каждой записи объекта. Мы проверим, является ли объект новым и не заполнен ли наш дополнительный реквизит, после чего присвоим ему сгенерированный код.
// В общем модуле расширения (например, УстановкаДопРеквизитовСервер)
// Этот модуль должен быть с галками "Сервер", "ВызовСерверногоМетода"
&НаСервере
Процедура ПриПослеЗаписиНоменклатуры(Объект, Отказ, ПараметрыЗаписи)
// Эта процедура выполняется на сервере после записи объекта.
// Объект.Ссылка уже заполнена, даже если объект был новым.
// Теперь мы можем работать с регистром дополнительных сведений.
// Получаем набор свойств для данного объекта.
// Функция ДополнительныеСведения.ПолучитьНаборСвойств работает с уже записанным объектом.
НаборСвойств = ДополнительныеСведения.ПолучитьНаборСвойств(Объект.Ссылка);
Если НаборСвойств = Неопределено Тогда
// Если набор свойств не существует (редко, но возможно для нового объекта, который еще не имел доп. реквизитов)
НаборСвойств = ДополнительныеСведения.СоздатьНаборСвойств(Объект.Метаданные());
НаборСвойств.Объект = Объект.Ссылка;
КонецЕсли;
// Получаем текущее значение дополнительного реквизита "КодДляСайта"
// Если реквизит не существует в наборе, Получить вернет Неопределено.
ЗначениеКода = НаборСвойств.Получить("КодДляСайта");
// Мы устанавливаем код только в том случае, если он не был заполнен вручную
// и объект является новым (или мы хотим обновлять его при каждой записи, что нежелательно для последовательного номера).
// Проверяем, что объект был только что создан (для первого заполнения).
Если НЕ ЗначениеЗаполнено(ЗначениеКода) И Объект.ЭтоНовый Тогда
// Если реквизит пуст, генерируем и устанавливаем новый код
НовыйКод = ПолучитьСледующийКодДляСайта(Объект.Метаданные());
НаборСвойств.Установить("КодДляСайта", НовыйКод);
НаборСвойств.Записать(); // Записываем изменения в регистр сведений
КонецЕсли;
КонецПроцедуры
Прежде чем внедрять это решение, мы должны осознавать его потенциальные недостатки:
ЗначенияСвойствОбъектов) при большом количестве элементов (например, десятки или сотни тысяч) будет требовать полного перебора всех записей. Это приведет к значительным задержкам и может серьезно замедлить работу пользователей при создании новых элементов. 1С не индексирует произвольные строковые дополнительные реквизиты, что делает такие запросы крайне неэффективными.Рассмотрим альтернативное решение, которое является более производительным и надежным, особенно для интеграции с внешними системами, такими как веб-сайты. Это использование внутреннего Уникального Идентификатора (UUID) объекта 1С.
UUID (Universally Unique Identifier) — это глобально уникальный идентификатор, представляющий собой длинную строку из 36 символов (например, '123e4567-e89b-12d3-a456-426655440000'). 1С автоматически генерирует уникальный UUID для каждого нового элемента (справочника, документа и т.д.) при его создании. Этот идентификатор гарантированно уникален в пределах одной информационной базы и даже, с очень высокой вероятностью, среди всех существующих баз данных.
Для заполнения дополнительного реквизита UUID мы также будем использовать расширение и подписку на событие ПослеЗаписи.
// В общем модуле расширения (например, УстановкаДопРеквизитовСервер)
// Этот модуль должен быть с галками "Сервер", "ВызовСерверногоМетода"
&НаСервере
Процедура ПриПослеЗаписиНоменклатуры(Объект, Отказ, ПараметрыЗаписи)
// Эта процедура выполняется на сервере после записи объекта.
НаборСвойств = ДополнительныеСведения.ПолучитьНаборСвойств(Объект.Ссылка);
Если НаборСвойств = Неопределено Тогда
НаборСвойств = ДополнительныеСведения.СоздатьНаборСвойств(Объект.Метаданные());
НаборСвойств.Объект = Объект.Ссылка;
КонецЕсли;
// Получаем текущее значение дополнительного реквизита "КодДляСайта"
ЗначениеКода = НаборСвойств.Получить("КодДляСайта");
// Мы устанавливаем UUID только в том случае, если реквизит пуст
// и объект был только что создан (для первого заполнения).
Если НЕ ЗначениеЗаполнено(ЗначениеКода) И Объект.ЭтоНовый Тогда
// Получаем UUID текущего объекта и преобразуем его в строку
УникальныйИдентификаторОбъекта = Строка(Объект.УникальныйИдентификатор());
НаборСвойств.Установить("КодДляСайта", УникальныйИдентификаторОбъекта);
НаборСвойств.Записать(); // Записываем изменения в регистр сведений
КонецЕсли;
КонецПроцедуры
Мы проанализировали два подхода к решению задачи автоматической генерации кода для дополнительного реквизита. Каждый из них имеет свои сильные и слабые стороны.
Всегда взвешивайте требования к удобству пользователя против требований к производительности и надежности системы. В большинстве случаев для интеграции с внешними системами UUID оказывается более предпочтительным выбором.