Как задать вопрос пользователю перед записью документа и изменить его данные в 1С 8.3?

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

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

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

В чем причина проблемы?

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

Чтобы реализовать корректный диалог, нам необходимо:

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

Способ 1. Использование параметров записи формы (рекомендуемый подход)

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

Разберем по шагам клиентский код формы документа:


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

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

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


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

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

Способ 2. Использование реквизита формы в качестве флага

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

  1. Добавим в форму реквизит ПроверкаПередЗаписьюВыполнена с типом Булево (по умолчанию Ложь).
  2. В обработчике ПередЗаписью проверяем значение реквизита: если он равен Ложь, отменяем запись и вызываем диалог.
  3. В обработке оповещения устанавливаем флаг в Истина, при необходимости изменяем данные формы и вызываем метод ЭтотОбъект.Записать().
  4. В обработчике ПослеЗаписи или ПриЧтенииСозданииНаСервере сбрасываем флаг обратно в Ложь для будущих интерактивных изменений.

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

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

Важные нюансы и рекомендации

Разберем ключевые технические детали, которые необходимо учитывать при реализации подобной логики:

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