Как правильно программно заполнить проверяемый реквизит в 1С 8.3?

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

Разработка в системе «1С:Предприятие 8.3» часто преподносит сюрпризы, связанные с тонкостями клиент-серверного взаимодействия и жизненным циклом объектов. Типичная ситуация: у нас есть документ, в котором один из реквизитов (например, Исполнитель) проверяется на заполнение. Мы хотим автоматизировать работу пользователя и заполнять этот реквизит программно при проведении или записи документа, получая значение из табличной части (например, обращаясь через точку к реквизитам ссылочного типа). Однако при попытке сделать это в обработчике проверки заполнения мы сталкиваемся с тем, что на форме значение появляется, но платформа всё равно выдает ошибку: «Поле "Исполнитель" не заполнено».

В этой статье мы подробно разберем, почему возникает такое поведение платформы, проанализируем порядок вызова событий при записи и выработаем элегантное, соответствующее стандартам разработки фирмы «1С» решение этой задачи.

Выясняем причину: почему заполнение в проверке заполнения не работает?

Для начала проанализируем ситуацию, с которой столкнулся автор темы. Попытка присвоить значение реквизиту в процедуре ОбработкаПроверкиЗаполненияНаСервере управляемой формы действительно приводит к визуальному заполнению поля на клиенте. Но почему система считает его пустым?

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

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

Более того, согласно стандартам разработки «1С», обработчики проверки заполнения предназначены исключительно для анализа корректности данных. Модифицировать данные объекта в этих процедурах категорически запрещено. Для изменения данных существуют специализированные события записи.

Анализируем жизненный цикл записи документа

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

  1. Клиент: Вызывается процедура ПередЗаписью на форме.
  2. Сервер (Валидация): Вызывается ОбработкаПроверкиЗаполненияНаСервере в модуле формы.
  3. Сервер (Валидация): Вызывается ОбработкаПроверкиЗаполнения в модуле объекта. Здесь платформа проверяет массив ПроверяемыеРеквизиты. Если транзакция прерывается из-за ошибок заполнения, дальнейшие шаги не выполняются!
  4. Сервер (Запись): Открывается транзакция базы данных.
  5. Сервер (Запись): Вызывается ПередЗаписьюНаСервере в модуле формы.
  6. Сервер (Запись): Вызывается ПередЗаписью в модуле объекта.
  7. Сервер (Запись): Происходит физическая запись в СУБД.

Как мы видим, проверка заполнения выполняется до открытия транзакции записи и до вызова процедур записи. Если мы попробуем заполнить реквизит в событии ПередЗаписьюНаСервере, платформа просто не дойдет до этого шага, споткнувшись о пустой обязательный реквизит на этапе проверки!

Отсюда вытекает единственно верное архитектурное решение задачи, состоящее из двух шагов:

Шаг 1. Исключаем реквизит из автопроверки

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

Используем обработчик ОбработкаПроверкиЗаполнения. Посмотрим на пример кода:


&После("ОбработкаПроверкиЗаполнения")
Процедура Расш1_ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    
    // Если в табличной части есть данные, значит, мы сможем заполнить реквизит программно.
    // В этом случае снимаем с платформы обязанность проверять его заполненность.
    Если РесурсыПредприятия.Количество() > 0 Тогда
        
        ИндексРеквизита = ПроверяемыеРеквизиты.Найти("Исполнитель");
        Если ИндексРеквизита <> Неопределено Тогда
            ПроверяемыеРеквизиты.Удалить(ИндексРеквизита);
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Если автоматическое заполнение возможно только при определенных условиях, мы обязательно проверяем эти условия (в нашем примере — наличие строк в табличной части РесурсыПредприятия). Если условия не выполняются, реквизит останется в массиве ПроверяемыеРеквизиты, и платформа выдаст стандартное предупреждение пользователю.

Шаг 2. Программное заполнение реквизита на сервере

Теперь, когда проверка успешно пройдена, нам нужно гарантированно записать значение в базу данных. Поскольку для получения значения нам требуется обратиться через точку к реквизиту справочника (СтрокаТЧ.РесурсПредприятия.ЗначениеРесурса), делать это необходимо строго на сервере. На клиенте подобные обращения вызовут ошибку производительности или падение системы.

У нас есть два отличных варианта реализации этого шага.

Вариант А. Заполнение в модуле формы (интерактивный режим)

Если автозаполнение должно работать только тогда, когда пользователь нажимает кнопку «Записать» или «Провести» на форме документа, идеальным местом будет обработчик ПередЗаписьюНаСервере управляемой формы.

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

Рассмотрим пример реализации:


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

Вариант Б. Заполнение в модуле объекта (универсальный режим)

Если документ может создаваться и записываться не только вручную пользователем, но и программно (например, через интеграции, веб-сервисы, групповые обработки или фоновые задания), то код формы не отработает. В таком случае логику заполнения следует перенести в модуль самого объекта (в процедуру ПередЗаписью) через расширение конфигурации.

Проанализируем пример кода для модуля объекта:


&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Обязательно проверяем, не идет ли загрузка данных (обмен)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Выполняем заполнение реквизита непосредственно перед записью в базу данных
    Если РесурсыПредприятия.Количество() > 0 Тогда
        ПерваяСтрока = РесурсыПредприятия[0];
        Исполнитель = ПерваяСтрока.РесурсПредприятия.ЗначениеРесурса;
    КонецЕсли;
    
КонецПроцедуры

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

Резюмируем правила хорошего тона при работе с реквизитами

Для предотвращения подобных ошибок в будущем, всегда придерживайтесь следующих правил при разработке в среде «1С:Предприятие 8»:

Применяя описанный двухшаговый шаблон, мы получаем чистое, стабильное и полностью соответствующее стандартам «1С» решение, которое избавит пользователей от раздражающих ложных предупреждений системы, а базу данных — от незаполненных аналитических разрезов.

← На главную