В разработке на платформе 1С часто возникает задача: прежде чем совершить критически важное действие (например, запись лога в регистр сведений), необходимо получить подтверждение от пользователя. Если пользователь нажимает "Да", система выполняет алгоритм; если "Нет" или закрывает окно — действие отменяется. Несмотря на кажущуюся простоту, при реализации этой задачи в среде 1С программисты часто сталкиваются с трудностями, связанными с контекстом выполнения кода и режимом модальности.
В данной статье мы подробно разберем, как правильно организовать этот процесс, проанализируем типичные ошибки и предложим оптимальные варианты кода для различных режимов работы форм.
Прежде чем переходить к коду, выясним причину, по которой старые методы могут не работать. В 1С существует два подхода к отображению диалогов:
Если вы работаете в старой конфигурации или в обычном приложении, где разрешена модальность, решение будет выглядеть максимально просто. Рассмотрим пример:
ТекстВопроса = "Записать изменения в лог редактирования?";
Ответ = Вопрос(ТекстВопроса, РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
// Здесь размещаем логику записи в регистр
НаборЗаписей = РегистрыСведений.ЛогРедактированияДокументов.СоздатьНаборЗаписей();
// ... заполнение и запись
НаборЗаписей.Записать();
Сообщить("Запись успешно выполнена");
КонецЕсли;
Важный момент: этот код будет работать только в том случае, если в свойствах вашей конфигурации разрешены модальные окна. Если же вы получаете ошибку об использовании модальности, необходимо использовать ОписаниеОповещения.
Проанализируем ситуацию, когда нам нужно использовать асинхронный вызов. Основная проблема автора темы заключалась в том, что он пытался проверить результат выполнения ПоказатьВопрос в той же процедуре, где вызывался сам вопрос. В асинхронной модели это невозможно.
Для решения задачи нам потребуется разделить алгоритм на две части: вызов вопроса и обработка ответа. Разберем по шагам.
Шаг 1. Подготовка вызова. В основной процедуре (например, при нажатии кнопки «Записать») мы создаем объект ОписаниеОповещения. В нем мы указываем имя процедуры, которая «подхватит» управление после закрытия диалога.
&НаКлиенте
Процедура КомандаЗаписатьВЛог(Команда)
// Подготавливаем оповещение.
// "ПослеЗакрытияВопроса" - имя нашей процедуры-обработчика.
// ЭтотОбъект - контекст, где эта процедура находится (модуль формы).
Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Вы подтверждаете запись данных?", РежимДиалогаВопрос.ДаНет);
// ВНИМАНИЕ: Код, написанный здесь, выполнится СРАЗУ,
// не дожидаясь ответа пользователя. Здесь ничего писать не нужно.
КонецПроцедуры
Шаг 2. Обработка ответа. Теперь создадим ту самую процедуру ПослеЗакрытияВопроса. Она обязательно должна иметь префикс Экспорт, иначе платформа не сможет её вызвать.
&НаКлиенте
Процедура ПослеЗакрытияВопроса(Результат, ДополнительныеПараметры) Экспорт
// Параметр "Результат" содержит кнопку, которую нажал пользователь
Если Результат = КодВозвратаДиалога.Да Тогда
// Переходим к записи в регистр
ВыполнитьЗаписьВРегистрНаСервере();
Иначе
Сообщить("Действие отменено пользователем.");
КонецЕсли;
КонецПроцедуры
Часто для записи в регистр нам нужны данные из текущего контекста (например, ссылка на товар или количество). Поскольку асинхронная процедура выполняется «позже», данные могут потеряться. Для этого используйте третий параметр ОписаниеОповещения — ДополнительныеПараметры. Посмотрим на пример:
// Создаем структуру с данными
ДанныеДляЗаписи = Новый Структура;
ДанныеДляЗаписи.Вставить("Товар", ТекущиеДанные.Номенклатура);
ДанныеДляЗаписи.Вставить("Количество", ТекущиеДанные.Количество);
// Передаем структуру в оповещение
Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтотОбъект, ДанныеДляЗаписи);
ПоказатьВопрос(Оповещение, "Записать лог?", РежимДиалогаВопрос.ДаНет);
В процедуре-обработчике эти данные будут доступны через параметр ДополнительныеПараметры.
Рассмотрим подробнее сам процесс записи в регистр. В сообщениях форума автор использовал НаборЗаписей. Это оправдано, если вам нужно записать сразу несколько строк. Однако, если вы логируете одно событие, удобнее и безопаснее использовать МенеджерЗаписи.
Почему это лучше? МенеджерЗаписи работает с одной конкретной записью и не требует сложной настройки отборов, как НаборЗаписей, где по ошибке можно стереть лишние данные в регистре.
&НаСервере
Процедура ВыполнитьЗаписьВРегистрНаСервере(СтруктураДанных)
МенеджерЗаписи = РегистрыСведений.ЛогРедактированияДокументов.СоздатьМенеджерЗаписи();
// Заполняем измерения (обязательно!)
МенеджерЗаписи.Период = ТекущаяДата();
МенеджерЗаписи.Документ = Ссылка;
МенеджерЗаписи.Товар = СтруктураДанных.Товар;
МенеджерЗаписи.Пользователь = ПараметрыСеанса.ТекущийПользователь;
// Заполняем ресурсы
МенеджерЗаписи.Количество = СтруктураДанных.Количество;
// Выполняем запись
Попытка
МенеджерЗаписи.Записать();
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Ошибка записи лога", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(Инфо));
КонецПопытки;
КонецПроцедуры
Проанализировав сообщения автора, мы заметили типичную ошибку в работе с выборкой запроса:
Пока Выборка.Следующий() Цикл КонецЦикла;
Такой цикл проходит по всем записям и ничего не делает, оставляя указатель выборки в самом конце. Чтобы данные попали в регистр, запись должна происходить внутри этого цикла. Выясним, как это должно выглядеть правильно:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Каждая итерация цикла - это новая строка для регистра
Набор = РегистрыСведений.ЛогРедактированияДокументов.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(Ссылка); // Если регистр подчинен регистратору
НоваяЗапись = Набор.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, Выборка);
Набор.Записать(Ложь); // Ложь означает, что мы не перезаписываем весь регистр по этому регистратору
КонецЦикла;
РегистрыСведений.Имя.Форма.ФормаСписка. Всегда используйте ЭтотОбъект или ЭтаФорма, если процедура находится в том же модуле. Платформа ищет процедуру в конкретном открытом экземпляре формы, а не в абстрактном объекте метаданных.Экспорт обязателен.ПоказатьВопрос) работают только &НаКлиенте, а работа с базой данных (Записать()) должна происходить &НаСервере.Резюмируя: для реализации записи по кнопке "Да", используйте ПоказатьВопрос совместно с ОписаниеОповещения. Передавайте все необходимые данные через ДополнительныеПараметры и выполняйте фактическую запись на сервере, предварительно проверив результат ответа пользователя. Такой подход обеспечит стабильную работу кода как в обычном, так и в управляемом приложении.