Как отловить момент записи документа и проверить заполнение полей, если документ еще не создан в базе?

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

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

Понимание контекста: данные в памяти против данных в базе

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

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

Метод 1: Использование события «ОбработкаПроверкиЗаполнения»

Это наиболее правильный и современный способ валидации данных. Данное событие предназначено специально для контроля корректности заполнения. Оно вызывается автоматически при записи объекта (если в свойствах реквизитов установлено «Выдавать ошибку») или программно методом ПроверитьЗаполнение().

Рассмотрим пример кода в модуле объекта, который проверяет заполнение реквизитов Содержание и Контрагент, а также анализирует табличную часть Контакты:


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

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

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

Метод 2: Событие «ПередЗаписью» в модуле объекта

Если логика проверки сложнее, чем просто «заполнено/пусто» (например, требуется сравнение с данными других документов или контроль остатков), используем обработчик ПередЗаписью. Выясним причину, почему это событие критически важно: оно выполняется внутри транзакции записи. Если мы установим параметр Отказ = Истина, транзакция будет отменена, и данные не сохранятся.

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


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

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

Метод 3: Проверка на форме (Клиентская валидация)

Рассмотрим ситуацию, когда перед записью нужно выдать не просто ошибку, а предупреждение с вопросом («Поле Содержание не заполнено, продолжить запись?»). Это невозможно сделать в модуле объекта, так как серверный код не поддерживает диалоговые окна. В этом случае мы используем событие формы ПередЗаписью.

Разберем алгоритм действий на клиенте:

  1. Пользователь нажимает «Записать».
  2. Срабатывает ПередЗаписью на клиенте в модуле формы.
  3. Проверяем условия. Если нужно задать вопрос — используем ПоказатьВопрос или ПоказатьПодтверждение.
  4. Важно: запись в управляемых формах асинхронна. Чтобы прервать запись, задать вопрос и продолжить её, нужно использовать специальный механизм с флагом-оповещением.

Пример упрощенной проверки в модуле формы:


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

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

Работа с подписками на события

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

Основные правила работы с подписками:

Пример кода в общем модуле для подписки:


Процедура ПроверитьЗаполнениеСобытияПередЗаписью(Источник, Отказ) Экспорт
	
	// Источник - это наш ДокументОбъект.Событие
	Если ТипЗнч(Источник) = Тип("ДокументОбъект.Событие") Тогда
		Если Источник.Контакты.Количество() > 0 И ПустаяСтрока(Источник.Содержание) Тогда
			// Логика проверки...
			// Отказ = Истина;
		КонецЕсли;
	КонецЕсли;

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

Порядок выполнения событий: когда ловить момент?

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

На стороне клиента (в форме):

  1. ПередЗаписью (здесь можно отменить запись или задать вопрос пользователю).

На стороне сервера (в объекте):

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

Выясним причину, почему не стоит использовать событие ПриЗаписи для валидации: к этому моменту данные уже отправлены в базу, и отмена записи на этом этапе «дороже» для производительности системы, чем на этапе ПередЗаписью.

Итоги

Мы выяснили, что отсутствие ссылки у нового документа не является препятствием для проверки его данных. Используя ЭтотОбъект.ТабличнаяЧасть, мы имеем полный доступ ко всему содержимому документа в оперативной памяти. Для стандартных проверок «на пустоту» рекомендуем использовать ОбработкаПроверкиЗаполнения, а для сложной бизнес-логики и отмены транзакций — ПередЗаписью в модуле объекта или через подписки на события.

← На главную