Как получить текущие значения реквизитов формы в модуле объекта при записи?

Программист
← На главную

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

Разбираемся в разнице между ЭтотОбъект и Ссылка

Для начала проанализируем ситуацию, описанную в теме. Когда пользователь нажимает кнопку «Записать» в управляемой форме, данные из реквизитов формы копируются в реквизиты объекта, и управление передается на сервер в модуль объекта. В процедуре ПередЗаписью у нас есть два основных способа обращения к данным:

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

Важный момент: До того как транзакция записи будет завершена, запрос к базе данных по ссылке будет возвращать «старое» значение — то, которое было сохранено до текущей правки. Поэтому, если ваша логика проверки должна основываться на том, что пользователь ввел в интерфейсе прямо сейчас, необходимо использовать исключительно ЭтотОбъект.

Почему запрос в модуле объекта не видит изменений?

Рассмотрим пример из обсуждения. Разработчик пытается проверить дубликаты Email в табличной части, используя запрос. Проблема в том, что запрос в 1С всегда обращается к данным, уже записанным в физические таблицы базы данных (или находящимся в кеше). Поскольку текущие изменения в табличной части ЭтотОбъект.КонтактнаяИнформация еще не «улетели» в таблицы БД, любой запрос, фильтрующий данные по ссылке текущего объекта, будет видеть только старое состояние коллекции.

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


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

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

Передача контекста из формы через ДополнительныеСвойства

Иногда в модуле объекта нужно знать не просто значения реквизитов, а некий контекст из формы, который не сохраняется в базе. Например, флаг «Игнорировать проверку дублей» или специфическое состояние элементов управления. Для этого идеально подходит коллекция ДополнительныеСвойства.

Разберем по шагам, как это реализовать:

Шаг 1. В модуле формы перехватываем запись на сервере:


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

Шаг 2. В модуле объекта считываем этот флаг:


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

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

Оптимизация: избавляемся от запроса в цикле

В сообщении №11 был приведен пример кода с запросом внутри цикла. Это критическая ошибка производительности («Запрос в цикле»). Если в табличной части будет 50 строк, система сделает 50 обращений к СУБД, что существенно замедлит работу. Выясним, как сделать это правильно: сначала соберем все данные, а потом выполним один запрос.

Проанализируем правильный алгоритм проверки дублей Email:


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

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

КонецПроцедуры

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

Для корректной работы с данными формы в модуле объекта придерживайтесь следующих правил:

  1. Всегда используйте ЭтотОбъект (или переданную переменную объекта) для анализа реквизитов и табличных частей, если вам нужны актуальные данные, еще не сохраненные в базу.
  2. Забудьте про обращение через точку от Ссылки внутри ПередЗаписью, если вы хотите увидеть изменения. Ссылка — это всегда «вчерашний день» объекта.
  3. Используйте ДополнительныеСвойства для передачи «сигналов» из формы в модуль объекта.
  4. Избегайте запросов в циклах. Подготовьте данные в памяти и передайте их в запрос как параметры массива или временной таблицы.
  5. Помните про предопределенные значения. На сервере используйте ПредопределенноеЗначение("Справочник.ВидыКИ.Email") или прямое обращение к менеджеру, это работает быстрее и надежнее строковых литералов.

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

← На главную