Как автоматически генерировать последовательный код для дополнительного реквизита "Код для сайта" в 1С:УНФ?

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

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

Основные подходы к генерации кода для дополнительного реквизита

Мы предлагаем рассмотреть два основных подхода к реализации автоматической генерации последовательного кода для дополнительного реквизита:

  1. Прямая генерация последовательного кода через обработчик события в расширении: Этот подход максимально соответствует исходной задаче, но сопряжен с определенными рисками и ограничениями производительности.
  2. Использование внутреннего Уникального Идентификатора (UUID) объекта 1С: Это альтернативное, более надежное и производительное решение, которое, однако, не предоставляет "красивых" последовательных номеров.

1. Генерация последовательного кода через обработчик события в расширении

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

Шаг 1: Создание дополнительного реквизита

Перед тем как приступить к написанию кода, необходимо создать сам дополнительный реквизит "Код для сайта" в режиме "Предприятие" через раздел Настройки -> Общие настройки -> Дополнительные реквизиты и сведения. Убедитесь, что он имеет тип "Строка" и привязан к нужному объекту (например, "Справочник.Номенклатура").

Шаг 2: Создание расширения конфигурации

Для реализации нашей задачи без изменения типовой конфигурации мы будем использовать расширение. Если у вас еще нет расширения, создайте его в конфигураторе:

  1. Откройте конфигурацию в режиме Конфигуратор.
  2. Перейдите в меню Конфигурация -> Расширения конфигурации.
  3. Нажмите кнопку Добавить, укажите имя расширения и, при необходимости, другие параметры.

Шаг 3: Добавление обработчика события

Наиболее подходящим местом для генерации кода для *нового* элемента является событие ПослеЗаписиНаСервере основного объекта (например, справочника "Номенклатура"). Почему именно ПослеЗаписиНаСервере? Потому что на момент выполнения этого события объект уже имеет свою ссылку (Объект.Ссылка), что критически важно для работы с регистром дополнительных сведений.

Нам потребуется создать подписку на событие ПослеЗаписи для того объекта, для которого создан дополнительный реквизит. Например, если реквизит создан для справочника Номенклатура, то подписываемся на его событие.

  1. В созданном расширении добавьте новый объект ПодпискаНаСобытие.
  2. Укажите имя, например, УстановкаКодаСайтаДляНоменклатуры.
  3. В поле Источник выберите Справочник.Номенклатура (или другой объект, для которого вы создали реквизит).
  4. В поле Событие выберите ПослеЗаписи.
  5. В поле Обработчик укажите имя нового общего модуля, например, УстановкаДопРеквизитовСервер, и имя процедуры в нем, например, ПриПослеЗаписиНоменклатуры.

Шаг 4: Реализация логики генерации и присвоения кода

Теперь мы перейдем к написанию кода в созданном общем модуле. Нам понадобятся две основные функции:

  1. Функция, которая ищет максимальный существующий код и формирует следующий.
  2. Процедура-обработчик, которая будет вызвана подпиской и присвоит сгенерированный код.

Функция для получения следующего кода:

Эта функция будет выполнять запрос к регистру сведений ЗначенияСвойствОбъектов, чтобы найти все ранее сгенерированные коды для сайта, извлечь из них числовую часть, определить максимальное значение и сформировать следующий код в нужном формате ("r000001").


// В общем модуле расширения (например, УстановкаДопРеквизитовСервер)
// Этот модуль должен быть с галками "Сервер", "ВызовСерверногоМетода"

&НаСервере
Функция ПолучитьСледующийКодДляСайта(МетаданныеОбъекта)
    МаксимальныйНомер = 0;
    
    // Шаг 1: Найдем ссылку на элемент плана видов характеристик по имени "КодДляСайта"
    // Это необходимо, чтобы точно идентифицировать дополнительный реквизит в регистре.
    ЗапросСвойства = Новый Запрос;
    ЗапросСвойства.Текст =
        "ВЫБРАТЬ
        |    ДополнительныеРеквизитыИСведения.Ссылка
        |ИЗ
        |    ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения КАК ДополнительныеРеквизитыИСведения
        |ГДЕ
        |    ДополнительныеРеквизитыИСведения.Имя = &ИмяСвойства";
    ЗапросСвойства.УстановитьПараметр("ИмяСвойства", "КодДляСайта");
    
    РезультатСвойства = ЗапросСвойства.Выполнить().Получить();
    Если РезультатСвойства = Неопределено Тогда
        // Если дополнительный реквизит с таким именем не найден, возвращаем начальный код.
        // В реальной системе здесь можно было бы сгенерировать ошибку или записать в журнал.
        Возврат "r000001"; 
    КонецЕсли;
    СсылкаНаСвойство = РезультатСвойства.Ссылка;

    // Шаг 2: Выполним запрос к регистру ЗначенияСвойствОбъектов
    // для поиска всех значений нашего дополнительного реквизита.
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ
        |    ЗначенияСвойствОбъектов.Значение КАК ЗначениеКода
        |ИЗ
        |    РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
        |ГДЕ
        |    ЗначенияСвойствОбъектов.Свойство = &СсылкаНаСвойство
        |    И ЗначенияСвойствОбъектов.Объект.Ссылка В
        |            (ВЫБРАТЬ РАЗЛИЧНЫЕ
        |                ВТ_Объекты.Ссылка
        |            ИЗ
        |                &ИмяТаблицыОбъекта КАК ВТ_Объекты)"; // Динамический выбор таблицы объекта

    Запрос.УстановитьПараметр("СсылкаНаСвойство", СсылкаНаСвойство);
    // Для фильтрации по типу объекта (например, Справочник.Номенклатура)
    Запрос.УстановитьПараметр("ИмяТаблицыОбъекта", МетаданныеОбъекта.ПолноеИмя()); 

    Результат = Запрос.Выполнить().Выгрузить();

    // Шаг 3: Проанализируем полученные значения и найдем максимальный номер
    Для Каждого СтрокаТЗ Из Результат Цикл
        КодСайта = Строка(СтрокаТЗ.ЗначениеКода);
        // Проверяем, что код начинается с "r" и соответствует формату
        Если Лев(КодСайта, 1) = "r" И СтрДлина(КодСайта) > 1 Тогда
            Попытка
                // Извлекаем числовую часть кода (например, из "r000001" получаем 1)
                Номер = Число(Сред(КодСайта, 2));
                Если Номер > МаксимальныйНомер Тогда
                    МаксимальныйНомер = Номер;
                КонецЕсли;
            Исключение
                // Игнорируем значения, которые не удается преобразовать в число.
                // Это позволяет избежать ошибок, если в поле присутствуют данные, не соответствующие формату.
            КонецПопытки;
        КонецЕсли;
    КонецЦикла;

    // Шаг 4: Формируем новый код с увеличенным на 1 номером
    // Функция Формат позволяет дополнить число нулями до нужной длины (в данном случае 6 знаков).
    Возврат "r" + Формат(МаксимальныйНомер + 1, "ЧЦ=7; ЧН=; ЧВН=; ЧФ=0");
КонецФункции

Процедура-обработчик подписки:

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


// В общем модуле расширения (например, УстановкаДопРеквизитовСервер)
// Этот модуль должен быть с галками "Сервер", "ВызовСерверногоМетода"

&НаСервере
Процедура ПриПослеЗаписиНоменклатуры(Объект, Отказ, ПараметрыЗаписи)
    // Эта процедура выполняется на сервере после записи объекта.
    // Объект.Ссылка уже заполнена, даже если объект был новым.
    // Теперь мы можем работать с регистром дополнительных сведений.

    // Получаем набор свойств для данного объекта.
    // Функция ДополнительныеСведения.ПолучитьНаборСвойств работает с уже записанным объектом.
    НаборСвойств = ДополнительныеСведения.ПолучитьНаборСвойств(Объект.Ссылка);
    
    Если НаборСвойств = Неопределено Тогда
        // Если набор свойств не существует (редко, но возможно для нового объекта, который еще не имел доп. реквизитов)
        НаборСвойств = ДополнительныеСведения.СоздатьНаборСвойств(Объект.Метаданные());
        НаборСвойств.Объект = Объект.Ссылка;
    КонецЕсли;

    // Получаем текущее значение дополнительного реквизита "КодДляСайта"
    // Если реквизит не существует в наборе, Получить вернет Неопределено.
    ЗначениеКода = НаборСвойств.Получить("КодДляСайта");

    // Мы устанавливаем код только в том случае, если он не был заполнен вручную
    // и объект является новым (или мы хотим обновлять его при каждой записи, что нежелательно для последовательного номера).
    // Проверяем, что объект был только что создан (для первого заполнения).
    Если НЕ ЗначениеЗаполнено(ЗначениеКода) И Объект.ЭтоНовый Тогда 
        // Если реквизит пуст, генерируем и устанавливаем новый код
        НовыйКод = ПолучитьСледующийКодДляСайта(Объект.Метаданные());
        НаборСвойств.Установить("КодДляСайта", НовыйКод);
        НаборСвойств.Записать(); // Записываем изменения в регистр сведений
    КонецЕсли;
КонецПроцедуры

Важные ограничения и риски этого подхода:

Прежде чем внедрять это решение, мы должны осознавать его потенциальные недостатки:

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

2. Использование внутреннего Уникального Идентификатора (UUID) объекта 1С

Рассмотрим альтернативное решение, которое является более производительным и надежным, особенно для интеграции с внешними системами, такими как веб-сайты. Это использование внутреннего Уникального Идентификатора (UUID) объекта 1С.

Что такое UUID?

UUID (Universally Unique Identifier) — это глобально уникальный идентификатор, представляющий собой длинную строку из 36 символов (например, '123e4567-e89b-12d3-a456-426655440000'). 1С автоматически генерирует уникальный UUID для каждого нового элемента (справочника, документа и т.д.) при его создании. Этот идентификатор гарантированно уникален в пределах одной информационной базы и даже, с очень высокой вероятностью, среди всех существующих баз данных.

Преимущества использования UUID для "Кода для сайта":

  1. Гарантированная уникальность: UUID генерируются платформой 1С и абсолютно уникальны, что исключает проблему коллизий при параллельной работе.
  2. Высокая производительность: Получение UUID объекта не требует никаких запросов к базе данных и поиска по неиндексированным полям. Это мгновенная операция.
  3. Идеально для интеграции: UUID идеально подходят для использования в качестве внешнего ключа или уникального идентификатора при обмене данными с веб-сайтами или другими внешними системами. Они не зависят от внутренней нумерации и префиксов.
  4. Не требует сложной доработки: Реализация крайне проста и надежна.

Недостатки UUID:

  1. "Нечеловеческий" вид: UUID представляют собой длинные, нечитаемые строки символов. Они неудобны для запоминания, ручного ввода или быстрого поиска пользователем. Если "Код для сайта" нужен в первую очередь для визуального отображения и удобного ручного поиска, то UUID может быть не самым подходящим вариантом.

Реализация заполнения дополнительного реквизита UUID:

Для заполнения дополнительного реквизита UUID мы также будем использовать расширение и подписку на событие ПослеЗаписи.


// В общем модуле расширения (например, УстановкаДопРеквизитовСервер)
// Этот модуль должен быть с галками "Сервер", "ВызовСерверногоМетода"

&НаСервере
Процедура ПриПослеЗаписиНоменклатуры(Объект, Отказ, ПараметрыЗаписи)
    // Эта процедура выполняется на сервере после записи объекта.

    НаборСвойств = ДополнительныеСведения.ПолучитьНаборСвойств(Объект.Ссылка);
    Если НаборСвойств = Неопределено Тогда
        НаборСвойств = ДополнительныеСведения.СоздатьНаборСвойств(Объект.Метаданные());
        НаборСвойств.Объект = Объект.Ссылка;
    КонецЕсли;

    // Получаем текущее значение дополнительного реквизита "КодДляСайта"
    ЗначениеКода = НаборСвойств.Получить("КодДляСайта");

    // Мы устанавливаем UUID только в том случае, если реквизит пуст
    // и объект был только что создан (для первого заполнения).
    Если НЕ ЗначениеЗаполнено(ЗначениеКода) И Объект.ЭтоНовый Тогда
        // Получаем UUID текущего объекта и преобразуем его в строку
        УникальныйИдентификаторОбъекта = Строка(Объект.УникальныйИдентификатор());
        НаборСвойств.Установить("КодДляСайта", УникальныйИдентификаторОбъекта);
        НаборСвойств.Записать(); // Записываем изменения в регистр сведений
    КонецЕсли;
КонецПроцедуры

Заключение: какой подход выбрать?

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

  1. Если "Код для сайта" нужен исключительно для технической связи с сайтом, а не для ручного поиска или удобства пользователя, то использование внутреннего Уникального Идентификатора (UUID) объекта 1С является наиболее производительным, надежным и рекомендованным решением. Оно гарантирует уникальность и не создает нагрузки на базу данных.
  2. Если же последовательный "красивый" код критически важен для пользователей, которые будут его активно использовать для поиска и идентификации, то можно применить подход с генерацией последовательного номера через обработчик событий. Однако в этом случае вам придется принять риски снижения производительности и потенциальных проблем с уникальностью на больших объемах данных или при интенсивной работе нескольких пользователей. Для таких сценариев мы рекомендуем дополнительно продумать механизмы оптимизации (например, использование специализированного регистра для хранения последнего номера и индексации) или блокировки, что значительно усложнит разработку.

Всегда взвешивайте требования к удобству пользователя против требований к производительности и надежности системы. В большинстве случаев для интеграции с внешними системами UUID оказывается более предпочтительным выбором.

← На главную