Как передать значение из формы в модуль набора записей регистра сведений в обычных формах

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке в среде Обычных форм (ОФ) программисты часто сталкиваются с ситуацией, когда необходимо передать какой-то признак или флаг (например, факт копирования записи) из формы в модуль объекта или модуль набора записей. Попытка объявить экспортную переменную в модуле набора записей и присвоить ей значение в модуле формы обычно заканчивается неудачей: переменная остается в состоянии Неопределено. В этой статье мы разберем, почему так происходит, и рассмотрим правильные механизмы взаимодействия между интерфейсом и логикой базы данных.

Разбираем причину: почему экспортные переменные не работают

Основная причина кроется в архитектуре 1С. Когда мы работаем с формой списка или формой записи регистра сведений, платформа создает временные экземпляры программных объектов. Переменная, объявленная в модуле набора записей с ключевым словом Экспорт, доступна только для конкретного экземпляра объекта РегистрСведенийНаборЗаписей, с которым вы работаете программно.

В случае с обычными формами, форма списка и набор записей, который записывается в базу данных, часто "развязаны". Когда вы меняете значение экспортной переменной в коде формы, вы можете обращаться к одному экземпляру объекта, а при фактической записи платформа может инициализировать другой экземпляр или использовать внутренние механизмы, где ваши изменения не сохранятся. Особенно это актуально для регистров сведений, где форма может работать с МенеджеромЗаписи, а запись в итоге происходит через НаборЗаписей.

Решение №1: Использование коллекции ДополнительныеСвойства

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

Рассмотрим по шагам, как реализовать передачу флага копирования. Сначала в модуле формы записи (или в обработчике события списка) мы помещаем данные в структуру:


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

Теперь проанализируем, как принять эти данные в модуле набора записей. В обработчике ПередЗаписью модуля набора записей мы проверяем наличие нашего ключа:


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

Преимущество этого метода в том, что структура ДополнительныеСвойства передается вместе с объектом в транзакции записи, что гарантирует доступ к данным на стороне сервера.

Решение №2: Использование обработчика ОбработкаЗаполнения

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

Выясним, как это работает. При копировании в параметр ДанныеЗаполнения передается объект-источник. Проанализируем пример кода:


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

Проблема уникальности: работа с GUID и запросами

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

Важный момент: Если вы используете Новый УникальныйИдентификатор, вероятность коллизии (создания двух одинаковых ID) практически равна нулю. Однако, чтобы гарантировать корректность данных, иногда требуется проверка по базе данных.

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

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

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |   1
    |ИЗ
    |   РегистрСведений.ИмяВашегоРегистра КАК Рег
    |ГДЕ
    |   Рег.СлужебныйИдентификатор = &Идентификатор";

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

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

Подводим итоги

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

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

← На главную